﻿{ -----------------------------------------------------------------------------
  Unit Name   : YBDBCtrlSet
  Author      : Pegasus
  Date        : 2010-07-20
  Description : 数据感应控件设置
  下拉内存表     下拉控件设置
  History     : 2010-07-29 Modify
  ------------------------------------------- }

unit YBDBCtrlSet;

interface

uses Graphics, Controls, Forms, Windows, Messages, WinSock, Classes, SyncObjs,
  SysUtils, Menus, ExtCtrls, Dialogs, Variants, Provider, DBClient, Registry,
  TypInfo, jpeg, DB, StdCtrls, DateUtils, ShlObj, ActiveX, IniFiles, ShellAPI,
  DBCtrls, BaseChildcxGridBtnQueryForm, BaseForm, BaseDataForm, StrUtils,
  cxEdit, cxTextEdit, cxMaskEdit, cxDBLookupComboBox, cxDropDownEdit, cxGrid,
  cxGridLevel, cxContainer, cxDBEdit, cxLookupEdit, cxDBLookupEdit, cxTimeEdit,
  cxLabel, cxLookAndFeels, cxLookAndFeelPainters, cxCalendar, cxSpinEdit,
  cxCurrencyEdit, cxGridDBTableView, cxGridCustomView, cxGridDBBandedTableView,
  dxGDIPlusClasses, cxImage, cxClasses, cxProgressBar, cxGridCustomTableView,
  cxGridTableView, cxPC, cxDBLabel, cxCalc, cxCustomData, cxGroupBox,
  // cxMRUEdit,//cxData, //cxDBData,
  cxGridBandedTableView, cxCheckBox, cxRadioGroup, cxMemo, cxTreeView, ComCtrls,
  MisString, DSUtils, GlobalVar, ExcelUtils,
  cxGridExportLink, cxGraphics, Uni, MemDS;

// cxDBLookupComboBox, cxComboBox 使用
type
  TreeBTypeStruc = record
    BType: integer;
    BKind: integer;
    BModule: integer;
  end;

  pTreeBTypeStruc = ^TreeBTypeStruc;

  GroupStruc = record
    FieldCaption: string;
    FieldName: string;
    LayoutID: integer;
    GroupID: integer;
    TableName: string;
  end;

  pGroupStruc = ^GroupStruc;

  TEnterExitEvent = procedure(Sender: TObject) of object;

  TcxDBDateEditKeyDown = class(TComponent)
  private
    edtDateEdit: TcxDateEdit;
    procedure DBDateEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TcxDBLookupOnKeyUp = class(TComponent)
  private
    edtComboBox: TcxDBLookupComboBox;
    FedtField: TField;
    FShowField: string;
    FShowName: TcxDBLabel;
    popMenu: TPopupMenu;
    FFilter: {$IF CompilerVersion>23.0} String {$ELSE} widestring{$IFEND};
    DataSet: TDataSet;
    ListLookup: TDataSet;
    ListDsProvider: TDataSetProvider;
    ListDataSource: TDataSource; // 下拉数据源
    ListDataSet: TUniQuery; // TSimpleDataSet; //TClientDataSet; //下拉数据集
    muAdd: TMenuItem;
    muEdit: TMenuItem;
    muFind: TMenuItem;
    muEmpty: TMenuItem;
    FOldFiltered: Boolean;
    FOldFilter: widestring;
    FOldFilterRecord: TFilterRecordEvent;
    FOldChange: TNotifyEvent;
    FOldPopup: TNotifyEvent;
    FOldCloseup: TNotifyEvent;
    FOldEnter: TEnterExitEvent;
    FOldExit: TEnterExitEvent;
    FLookupID: string; // 记录下拉数据集的ID ts_LookupTables 为处理新增，修改打开对应的编辑窗口
    FOldKeyPreview: Boolean;
    FFixDropFilter: Boolean;
    FBPopMenu: Boolean;
    FFindHint: widestring;
    FFindText: widestring;
    FFindMemo: TcxMemo;
    FParentForm: TfrmBaseForm;
    procedure ShowNameClick(Sender: TObject);
    procedure ShowFindHint;
    procedure DBLookupComboxKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure DBLookupComboxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure DBLookupComboxFilterRecord(DataSet: TDataSet; var Accept: Boolean);
    // procedure DBLookupComboBoxChange(Sender: TObject);
    procedure DBLookupComboBoxCloseup(Sender: TObject);
    procedure DBLookupComboBoxPopup(Sender: TObject);
    procedure DBLookupComboBoxEnter(Sender: TObject);
    procedure DBLookupComboBoxExit(Sender: TObject);
    function FindPy(const sSubStr: widestring): Boolean;
    function ParentForm: TfrmBaseForm;
    procedure ButtonClick(Sender: TObject; AButtonIndex: integer);
    procedure pmNewClick(Sender: TObject);
    procedure pmEditClick(Sender: TObject);
    procedure pmFindClick(Sender: TObject);
    procedure pmEmptyClick(Sender: TObject);
    procedure SortingChanged(Sender: TObject);
    procedure PopLookup(EditType: TEditType);
    procedure SetDropDownFilter(const Value: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND});
    function GetedtField: TField;
    property edtField: TField read GetedtField;
  public
    constructor Create(AOwner: TComponent; LookupID: string; AShowStyle: TLookupComboxShowStyle;
      ShowField: string);
      reintroduce;
    destructor Destroy; override;
    procedure RefreshData(RefreshLookupData: Boolean = True);
    procedure ShowName(Show: Boolean);
    procedure SynShowNamePos();
    property DropDownFilter: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND} read FFilter write SetDropDownFilter;
    property FixDropFilter: Boolean read FFixDropFilter write FFixDropFilter;
  end;

  TcxDBColumnOnChange = class(TComponent) // TcxDBColumnLookupComboBoxOnChange
  private
    function GetDataSet: TDataSet;
  private
    edtColumn: TcxGridDBBandedColumn;
    edtComboBox: TcxLookupComboBox;
    FedtField: TField;
    FDataSet: TDataSet;
    popMenu: TPopupMenu;
    // DataSet: TDataSet;
    ListLookup: TDataSet;
    // ListDsProvider: TDataSetProvider;
    ListDataSource: TDataSource;
    ListDataSet: TUniQuery; // TSimpleDataSet; //TClientDataSet;
    muAdd: TMenuItem;
    muEdit: TMenuItem;
    muFind: TMenuItem;
    muEmpty: TMenuItem;
    // FOldEnter: TEnterExitEvent;
    // FOldExit: TEnterExitEvent;
    FLookupID: string; // 记录下拉数据集的ID ts_LookupTables 为处理新增，修改打开对应的编辑窗口
    FShowField: string;
    // FOldKeyPreview: Boolean;
    FFixDropFilter: Boolean;
    FOldFiltered: Boolean;
    FOldFilter: widestring;
    FOldFilterRecord: TFilterRecordEvent;
    FOldPopup: TNotifyEvent;
    FOldCloseup: TNotifyEvent;
    FFindHint: widestring;
    FFindText: widestring;
    FFindMemo: TcxMemo;
    FParentForm: TfrmBaseForm;
    FFilter: {$IF CompilerVersion>23.0} String {$ELSE} widestring{$IFEND};
    FListField: TStringList;
    FSetValue: Boolean;
    procedure ShowFindHint;
    function FindPy(const sSubStr: widestring): Boolean;
    procedure DBComboBoxColumnDblClick(Sender: TObject);
    procedure DBComboBoxColumnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure DBComboBoxColumnFilterRecord(DataSet: TDataSet; var Accept: Boolean);
    procedure DBComboBoxColumnEditValueChange(Sender: TObject);
    procedure DBComboBoxColumnInitPopup(Sender: TObject);
    procedure OnDisplayText(Sender: TcxCustomGridTableItem; ARecord: TcxCustomGridRecord;
      var AText: string);
    procedure DBComboBoxColumnCloseup(Sender: TObject);
    procedure DBComboBoxColumnValidate(Sender: TObject; var DisPlayValue: Variant;
      var ErrorText: TCaption;
      var Error: Boolean);
    procedure DBComboBoxColumnPopup(Sender: TObject);
    // procedure DBLookupComboBoxEnter(Sender: TObject);
    // procedure DBLookupComboBoxExit(Sender: TObject);
    procedure ButtonClick(Sender: TObject; AButtonIndex: integer);
    procedure pmNewClick(Sender: TObject);
    procedure pmEditClick(Sender: TObject);
    procedure pmFindClick(Sender: TObject);
    procedure pmEmptyClick(Sender: TObject);
    procedure SortingChanged(Sender: TObject);
    procedure PopLookup(EditType: TEditType);
    procedure SetDropDownFilter(const Value: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND});
    function GetedtField: TField;
    property edtField: TField read GetedtField;
    property DataSet: TDataSet read GetDataSet;
  public
    constructor Create(ParentForm: TfrmBaseForm; AOwner: TComponent; LookupID, ShowField: string);
      reintroduce;
    destructor Destroy; override;
    procedure OpenData();
    procedure RefreshData(RefreshLookupData: Boolean = True);
    procedure DblClick();
    property DropDownFilter: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND} read FFilter write SetDropDownFilter;
    // FixDropFilter:=False;只在下拉是触发过滤，保证表格浏览是显示Lookup数据
    property FixDropFilter: Boolean read FFixDropFilter write FFixDropFilter;
  end;

  TcxDBColumnComBoxOnChange = class(TComponent)
  private
  private
    DropDownList: TStringList;
    edtColumn: TcxGridDBBandedColumn;
    edtComboBox: TcxComboBox;
    FedtField: TField;
    // FOldEnter: TEnterExitEvent;
    // FOldExit: TEnterExitEvent;
    FLookupID: string; // 记录下拉数据集的ID ts_LookupTables 为处理新增，修改打开对应的编辑窗口
    // FOldKeyPreview: Boolean;
    FParentForm: TfrmBaseForm;
    procedure DBComboBoxColumnChange(Sender: TObject);
    // procedure DBLookupComboBoxEnter(Sender: TObject);
    // procedure DBLookupComboBoxExit(Sender: TObject);
    function GetedtField: TField;
{$HINTS OFF}
    property edtField: TField read GetedtField;
{$HINTS ON}
  public
    constructor Create(ParentForm: TfrmBaseForm; AOwner: TComponent;
      LookupID, LookupParam, TableName: string);
      reintroduce;
    destructor Destroy; override;
  end;

function FindPy(const sSubStr, sListStr: widestring): Boolean;

function GetUserRightFilter(FilterTable: string): string;

// 创建动态基础资料编辑窗体
function CreatesDynamicEditForm(AFormID: string; AFormCaption: string; ADLLFunction: TDLLFunction;
  AForm: TfrmBaseChildcxGridBtnQueryForm = nil): Boolean;
{ ---------------------------公共数据源----------------------------------------- }
function GetLookupTableIndex(ALookupID, AParam: string): integer; overload; // 传入关联编号，返回在全局关联数组的序号
function GetLookupTableIndex(ALookupCombox: TObject): integer; overload; // 传入关联编号，返回在全局关联数组的序号
function GetLookupDataSource(ALookupID, AParam: string): TDataSource;
function GetLokupDsNotOpen(ALookupID, AParam: string): TDataSource;
function GetLookupTable(ALookupID, AParam: string): TDataSet;
function GetLokupTbNotOpen(ALookupID, AParam: string): TDataSet;
procedure LookupTableRefresh(ALookupID, ALookupParam: string); // 关联表重新打开
{ ---------------------------下拉操作------------------------------------------- }
procedure SetLookupField(AField: TField; AKeyFieldName: string; ALookupID, ALookupParam: string);
// 设置关联字段属性，从全局关联数组里找
procedure SetLookupCombox(ALookupCombox: TObject; ALookupID, ALookupParam: string;
  AShowStyle: TLookupComboxShowStyle;
  ShowField: string = ''); // 设置关联控件属性，从全局关联数组里找
procedure SetLookupcxGridColumn(ParentForm: TfrmBaseForm; ADBColumn: TObject;
  ALookupID, ALookupParam: string;
  ShowField: string = ''); // 设置关联CXGRID属性，从全局关联数组里找
procedure SetDateEdit(ADBDateEdit: TObject);
procedure SetComboBox(Sender: TcxDBComboBox; LookupID, Param: string; TableName: string = '');
procedure SetComBox(Sender: TcxComboBox; LookupID, Param: string; TableName: string = '');
procedure SetComboBoxcxGridColumn(ParentForm: TfrmBaseForm; ADBColumn: TObject;
  ALookupID, ALookupParam: string;
  TableName: string = ''); // 设置关联CXGRID属性，从全局关联数组里找
procedure SetComboBoxSQL(Sender: TcxComboBox; SQL: string; ClearItem: Boolean = True);

{ ---------------------------cxGrid操作----------------------------------------- }
function GetFocusFieldName(Sender: TcxGridDBTableView): string; overload;
function GetFocusFieldName(Sender: TcxGridDBBandedTableView): string; overload;
function GetColWidth(Sender: TcxGridDBTableView; FieldName: string): integer; overload;
function GetColWidth(Sender: TcxGridDBBandedTableView; FieldName: string): integer; overload;
function GetColVisible(Sender: TcxGridDBTableView; FieldName: string): Boolean; overload;
function GetColVisible(Sender: TcxGridDBBandedTableView; FieldName: string): Boolean; overload;
function GetColCaption(Sender: TcxGridDBTableView; FieldName: string): string; overload;
function GetColCaption(Sender: TcxGridDBBandedTableView; FieldName: string): string; overload;
function GetBandByCaption(Sender: TcxGridDBBandedTableView; Caption: string): TcxGridBand;
function GetSelColRecText(Sender: TcxGridDBTableView; FullRecord: Boolean = False): string;
  overload;
function GetSelColRecText(Sender: TcxGridDBBandedTableView; FullRecord: Boolean = False)
  : string; overload;
procedure SetSelColRecText(FieldName: string; Value: Variant; Sender: TcxGridDBBandedTableView);

procedure AddFooterSum(Sender: TcxGridDBTableView; oADBColumn: TcxGridDBColumn;
  iFooterKind, iFooterSumType: integer;
  sDataType: string);

procedure SetNextColumnFocused(Sender: TcxGridDBTableView; NextIndex: integer = 1); overload;
procedure SetNextColumnFocused(Sender: TcxGridDBBandedTableView; NextIndex: integer = 1); overload;
procedure AddViewColumn(Sender: TcxGridDBTableView; ACaption, AFieldName: string;
  AWidth: integer); overload;
procedure AddViewColumn(Sender: TcxGridDBBandedTableView; ACaption, AFieldName: string;
  AWidth: integer); overload;

procedure SetColWidth(Sender: TcxGridDBTableView; FieldName: string; iWidth: integer); overload;
procedure SetColWidth(Sender: TcxGridDBBandedTableView; FieldName: string;
  iWidth: integer); overload;
procedure SetColVisible(Sender: TcxGridDBTableView; FieldName: string; Visible: Boolean); overload;
procedure SetColVisible(Sender: TcxGridDBBandedTableView; FieldName: string;
  Visible: Boolean); overload;
procedure SetColReadOnly(Sender: TcxGridDBTableView; FieldName: string; ReadOnly: Boolean);
  overload;
procedure SetColReadOnly(Sender: TcxGridDBBandedTableView; FieldName: string;
  ReadOnly: Boolean); overload;
procedure SetColIndex(Sender: TcxGridDBTableView; FieldName: string; iIndex: integer = 0); overload;
procedure SetColIndex(Sender: TcxGridDBBandedTableView; FieldName: string;
  iIndex: integer = 0); overload;
procedure SetColCaption(Sender: TcxGridDBTableView; FieldName, Caption: string); overload;
procedure SetColCaption(Sender: TcxGridDBBandedTableView; FieldName, Caption: string); overload;
procedure SetColBandsIndex(Sender: TcxGridDBBandedTableView; FieldName: string;
  iIndex: integer = 0); overload;

procedure SetGridEnterNew(Sender: TcxGrid);
procedure SetViewBand(Sender: TcxGridDBBandedTableView);
procedure SetViewColumnWidth(Sender: TcxGridDBTableView);
procedure SetViewColumn(Sender: TcxGridDBTableView; DataSet: TDataSet; BType, UserID: integer;
  Choice: Boolean = False);
procedure DrawFullGrid(Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
  AViewInfo: TcxGridTableDataCellViewInfo;
  var ADone: Boolean); // 画满格
// 左键点击了grid的空白新增记录
procedure EmptyCellClick(Sender: TcxCustomGridTableView; Button: TMouseButton; DataSet: TDataSet;
  X, Y: integer);
// 导出cxGrid导出Excel后设置数值型字段的字符格式
procedure SetViewToExcelColumn(Sender: TcxGrid; xlsReport: TxlsImExport);
procedure GridToExcel(var FileName: string; Sender: TcxGrid; SetNull: Boolean = True);
{ ---------------------------tree操作------------------------------------------- }
procedure LoadTree(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; const p_TableName: string;
  p_PId: integer;
  const FilterSQL: string = ''; t_ADOQuery: TUniQuery = nil);
procedure LoadTreeNoSubNode(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; p_TableName: string;
  p_PId: integer;
  KeyField: string = 'id';const FilterSQL: string = '');
procedure LoadTreeUserByDept(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; p_TableName: string;
  p_PId: integer);
{ ---------------------------部分系统参数下拉设置------------------------------- }
procedure LoadComboBoxItem(Sender: TcxComboBox; ParamName: string);

implementation

uses DataModule, BaseChildForm,
  GlobalInterface, BaseTreeForm, ParticularQueryForm, QueryChoiceProduct;

function GetUserRightFilter(FilterTable: string): string;
var
  bFilter, bAdminLookTraderUnSet: Boolean;
begin
  Result := '';
  bFilter := False;
  bAdminLookTraderUnSet := False;
  if (LowerCase(FilterTable) = 'td_depot') and not MainDM.GetUserInfo.IsAdmin and
    (GetSysParamValue('userlookselfdepot') = '1') then
    bFilter := True
  else if (Pos('td_trader', FilterTable) > 0) and not MainDM.GetUserInfo.IsAdmin and
    (GetSysParamValue('userlookselftrader') = '1') then
  begin
    bFilter := True;
    bAdminLookTraderUnSet := (GetSysParamValue('adminlooktraderunset') = '1');
  end
  else if (LowerCase(FilterTable) = 'te_shop') and not MainDM.GetUserInfo.IsAdmin then
    bFilter := True;
  if bFilter then
    if not(MainDM.GetUserInfo.IsAdmin) then
    begin
      Result := ' and (userright is null or userright=' + QuotedStr('') + ' or userright like ' +
        QuotedStr('%,' + IntToStr(MainDM.GetUserInfo.UserID) + ',%') + ')';
      if bAdminLookTraderUnSet then
        Result := ' and (userright like ' + QuotedStr('%,' + IntToStr(MainDM.GetUserInfo.UserID) +
          ',%') + ')';
    end;
end;

function CreatesDynamicEditForm(AFormID: string; AFormCaption: string; ADLLFunction: TDLLFunction;
  AForm: TfrmBaseChildcxGridBtnQueryForm = nil): Boolean;
var
  qyDataSet: TUniQuery;
  oForm: TfrmBaseChildcxGridBtnQueryForm;
  oADBColumn: TcxGridDBColumn;
  sAFieldName, sAFieldCaption, sTableName, sDataType, sDynFilter: string;
  iRow, iLabelWidth, i, iComponentWidth, iLayoutid: integer;
  oAComponent: TComponent;
  oALabel: TcxLabel;
  oControlProperty: PDataEditControlProperty;
  iComponentRow, iLookupIndex: integer;
  iComponentLeft, iComponentTop, iLabelLeft, iLabelTop, iColSpace: integer;
  bISInitOpenData, bShowModal: Boolean;
  // Radio Group
  m, n: integer;
  sValue, sTmp, sLookupID, sParameters: string;
  slValue: TStringList;
begin
  Result := False;
  bShowModal := False;
  oAComponent := nil;
  iLabelTop := 0;
  if AFormID = '' then
  begin
    ShowInfomationBox('没有设置窗体编号，请在系统模块设置里设置');
    exit;
  end;

  // 如果当前动态窗体已打开，则直接显示，动态窗体编号放在TAG属性中。
  for i := 0 to Screen.FormCount - 1 do
  begin
    if (Screen.Forms[i].Tag = StrToInt(AFormID)) and (Screen.Forms[i].Caption = AFormCaption) then
    begin
      ShowWindow(Screen.Forms[i].Handle, SW_SHOWNORMAL); // 显示窗口.
      Screen.Forms[i].BringToFront;
      UpdateWindow(Screen.Forms[i].Handle);
      exit;
    end;
  end;

  if AFormID = '8400' then
    AFormID := '6100'; // 工种  6100生产 8400工资
  if AFormID = '8410' then
    AFormID := '6110'; // 工序
  GetADOQuery(qyDataSet, 'SELECT * FROM ts_DynamicEditFormMst WHERE ET_EditFormID=' + AFormID);
  if qyDataSet.RecordCount < 1 then
  begin
    ShowInfomationBox('没有找到相应的动态窗体，请查看表ts_DynamicEditFormMst是否有定义');
    qyDataSet.Close;
    FreeAndNil(qyDataSet);
    exit;
  end;
  sTableName := qyDataSet.FieldByName('ET_Table_Name').AsString;
  bISInitOpenData := qyDataSet.FieldByName('ET_ISInitOpenData').AsBoolean;

  // 取最长的字须标题做Label宽度，每个汉字12象素
  iLabelWidth := GetValueSQL
    ('SELECT max(len(dt_fieldCaption)) maxCaptionLen FROM ts_DynamicEditFormDtl WHERE DT_EditFormID='
    + AFormID, 0);
  iLabelWidth := iLabelWidth * 12 + 10; // +6处理阴影
  oForm := AForm;
  if oForm = nil then
    oForm := TfrmBaseChildcxGridBtnQueryForm.Create(nil,
      qyDataSet.FieldByName('ET_FormCaption').AsString)
  else
    bShowModal := True;
  with oForm do
  begin
    GetADOQuery(qryDynMst, 'SELECT * FROM ts_DynamicEditFormMst WHERE ET_EditFormID=' + AFormID);
    Tag := StrToInt(AFormID);
    oForm.MyFormType := ftDynamicEditForm;
    oForm.KeyField := Trim(qyDataSet.FieldByName('ET_KeyFields').AsString);
    oForm.CodeField := Trim(qyDataSet.FieldByName('ET_CodeField').AsString);
    oForm.TableName := Trim(qyDataSet.FieldByName('ET_Table_Name').AsString);
    oForm.DtlTableName := Trim(qyDataSet.FieldByName('ET_DtlTableName').AsString);
    if oForm.DtlTableName <> '' then
    begin
      oForm.grdDtl.Visible := True;
      oForm.PanelData.Height := Trunc(oForm.Height / 2);
    end;
    labFuncName.Caption := qyDataSet.FieldByName('ET_FormCaption').AsString;
    PanelQuery.Visible := False;
    MainDataGridDBTableView.ClearItems;
    oForm.MyDllFunction := ADLLFunction;
    oForm.MainDataSource.DataSet.AfterScroll := nil;
    if bISInitOpenData and (not bShowModal) then
      LocateRecord(-1)
    else
      LocateRecord(-2);
    if (oForm.KeyField <> '') and (oForm.MainDataSource.DataSet.FindField(oForm.KeyField) <> nil)
    then
      oForm.MainDataSource.DataSet.FindField(oForm.KeyField).ProviderFlags :=
        oForm.MainDataSource.DataSet.FindField(oForm.KeyField).ProviderFlags +
        [pfInKey, pfInUpdate, pfInWhere];
    iRow := 0;
    iComponentRow := 0;
    iLayoutid := GetUserLayOut(StrToInt(AFormID), oForm.PUserInfo.UserID);
    sDynFilter := '';
    if (GetSysParamValue('linkFinance') <> '1') then
    begin
      if (AFormID = '7250') then // 储值卡类型
        sDynFilter := ' and dt_fieldname<>''dissubid'' and dt_fieldname<>''subid''';
      if (AFormID = '7270') then // 抵用券
        sDynFilter := ' and dt_fieldname<>''othersubid'' and dt_fieldname<>''subid''';
    end;
    qyDynDtl.Close;
    qyDynDtl.SQL.Text := 'SELECT * FROM ts_DynamicEditFormDtl ' + ' WHERE DT_EditFormID=' + AFormID +
      ' and dt_layoutid=' + IntToStr(iLayoutid) + sDynFilter + ' ORDER BY DT_ISFUllCol,DT_Index ';
    qyDynDtl.Open;
    qyDynDtl.Filtered := False;
    qyDynDtl.Filter := 'dt_isshow=true';
    qyDynDtl.Filtered := True;
    qyDynDtl.First;
    try
      while not qyDynDtl.Eof do
      begin
        // ********** 加载cxGrid的字段列 **************
        sAFieldName := Trim(qyDynDtl.FieldByName('DT_FieldName').AsString);
        sAFieldCaption := Trim(qyDynDtl.FieldByName('DT_FieldCaption').AsString);
        sDataType := qyDynDtl.FieldByName('DT_DataType').AsString;
        sLookupID := LowerCase(Trim(qyDynDtl.FieldByName('DT_LookupID').AsString));
        sParameters := LowerCase(Trim(qyDynDtl.FieldByName('DT_LookupParams').AsString));
        oADBColumn := nil;
        if (qyDynDtl.FieldByName('DT_FieldType').AsInteger = 1) or (not qryDtl.Active) then
        begin
          oADBColumn := MainDataGridDBTableView.CreateColumn;
          MainDataSource.DataSet.FieldByName(sAFieldName).DisplayLabel := sAFieldCaption;
        end
        else if (qyDynDtl.FieldByName('DT_FieldType').AsInteger = 2) and (qryDtl.Active) then
        begin
          oADBColumn := viewDtl.CreateColumn;
          qryDtl.FieldByName(sAFieldName).DisplayLabel := sAFieldCaption;
          if (sDataType = 'L') and (sLookupID <> '') then
          begin
            SetLookupcxGridColumn(oForm, oADBColumn, sLookupID, sParameters); // , lsListFields);
          end
          else if (sDataType = 'S') and (sLookupID <> '') then
          begin
            SetComboBoxcxGridColumn(oForm, oADBColumn, sLookupID, sParameters, oForm.DtlTableName);
          end
          else if (sDataType = 'UR') then
          begin
            oADBColumn.PropertiesClass := TcxTextEditProperties;
            with TcxTextEditProperties(oADBColumn.Properties).Buttons.Add do
              Kind := bkEllipsis;
            oADBColumn.Properties.OnButtonClick := URButtonClick;
            oADBColumn.Properties.ReadOnly := True;
          end
          else if sDataType = 'R' then
          begin
            oADBColumn.PropertiesClass := TcxRadioGroupProperties;
            sValue := '';
            m := Pos('{', sParameters);
            if (m > 0) then
            begin
              sParameters := Trim(Copy(sParameters, m + 1, Length(sParameters) - m));
              m := Pos(':', sParameters);
              while (m > 0) do
              begin
                sValue := Trim(Copy(sParameters, 1, m - 1));
                sTmp := '';
                n := Pos(';', sParameters);
                if (n > 0) then
                begin
                  sTmp := Copy(sParameters, m + 1, n - m - 1);
                  with TcxRadioGroupProperties(oADBColumn.Properties).Items.Add do
                  begin
                    Caption := Trim(sTmp);
                    Value := sValue;
                  end;
                  sParameters := Copy(sParameters, n + 1, Length(sParameters) - n);
                  m := Pos(':', sParameters);
                end
                else
                begin
                  sTmp := Copy(sParameters, m + 1, Length(sParameters) - m - 1);
                  with TcxRadioGroupProperties(oADBColumn.Properties).Items.Add do
                  begin
                    Caption := Trim(sTmp);
                    Value := sValue;
                  end;
                  break;
                end;
              end;
            end;
          end;
        end;
        oADBColumn.DataBinding.FieldName := sAFieldName;
        oADBColumn.Caption := Trim(qyDynDtl.FieldByName('DT_FieldCaption').AsString);
        oADBColumn.Width := qyDynDtl.FieldByName('DT_ColWidth').AsInteger;
        oADBColumn.MinWidth := Trunc(qyDynDtl.FieldByName('DT_ColWidth').AsInteger * 0.8);
        oADBColumn.Tag := qyDynDtl.FieldByName('DT_Index').AsInteger;
        if qyDynDtl.FieldByName('DT_FieldType').AsInteger = 1 then
        begin
          // 计算控件的LEFT，TOP，LABEL的宽度则取最大长度的标题，一行放4个控件
          iColSpace := 18; // 控件列之间的间隔
          iComponentWidth := 100; // 每行所占的像素
          if qyDynDtl.FieldByName('DT_ISFullCol').AsBoolean then // 如果控件占用整行
          begin
            Inc(iComponentRow);
            iComponentWidth := (iComponentWidth + iLabelWidth + iColSpace) * 3 + iComponentWidth;
            // 每行所占的像素 ，整行占用4列
            iLabelLeft := 10;
          end
          else
          begin
            iComponentRow := Trunc(iRow / 4);
            iLabelLeft := (iRow mod 4) * (iComponentWidth + iLabelWidth + iColSpace) + 10;
          end;
          iLabelTop := iComponentRow * 28 + 15;
          iComponentLeft := iLabelLeft + iLabelWidth + 2;
          iComponentTop := iLabelTop - 3;

          oALabel := TcxLabel.Create(oForm);
          oALabel.Caption := qyDynDtl.FieldByName('DT_FieldCaption').AsString;
          oALabel.Left := iLabelLeft;
          oALabel.Top := iLabelTop;
          oALabel.Width := iLabelWidth;
          oALabel.AutoSize := False;
          oALabel.Parent := PanelData;
          oALabel.Properties.Alignment.Horz := taRightJustify; // oALabel.Alignment := taRightJustify;
          oALabel.Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
          // 关联字段，从全局关联数组里取数据
          if (not qyDynDtl.FieldByName('DT_LookupID').IsNull) and
            (Trim(qyDynDtl.FieldByName('DT_LookupID').AsString) <> '') then
          begin
            if sDataType = 'L' then
            begin
              iLookupIndex := GetLookupTableIndex(qyDynDtl.FieldByName('DT_LookupID').AsString,
                qyDynDtl.FieldByName('DT_LookupParams').AsString);
              if iLookupIndex < 0 then
              begin
                ShowInfomationBox('动态编辑窗[' + AFormID + ']，字段[' + sAFieldName + ']加载关联数据集时找不到相应的数据表');
                exit;
              end;
              SetLookupcxGridColumn(oForm, oADBColumn, qyDynDtl.FieldByName('DT_LookupID').AsString,
                qyDynDtl.FieldByName('DT_LookupParams').AsString); // , lsListFields);
              oAComponent := TcxDBLookupComboBox.Create(oForm);
              with TcxDBLookupComboBox(oAComponent) do
              begin
                Name := 'dynedt' + sAFieldName; // Name在后面赋值Name会默认显示在Text
                DataBinding.DataSource := MainDataSource;
                DataBinding.DataField := sAFieldName;
                Parent := PanelData;
                Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
              end;
              SetLookupCombox(oAComponent, qyDynDtl.FieldByName('DT_LookupID').AsString,
                qyDynDtl.FieldByName('DT_LookupParams').AsString, lsListFields);
            end
            else if (sDataType = 'S') or (sDataType = 'SC') then
            begin
              oAComponent := TcxDBComboBox.Create(oForm);
              with TcxDBComboBox(oAComponent) do
              begin
                Name := 'dynedt' + sAFieldName; // Name在后面赋值Name会默认显示在Text
                Parent := PanelData;
                DataBinding.DataSource := MainDataSource;
                DataBinding.DataField := sAFieldName;
                Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
                oForm.SetDynDBComboBox(TcxDBComboBox(oAComponent), qyDynDtl);
              end;
            end;
          end
          else if sDataType = 'R' then
          begin
            oAComponent := TcxDBRadioGroup.Create(oForm);
            oADBColumn.PropertiesClass := TcxRadioGroupProperties;
            with TcxDBRadioGroup(oAComponent) do
            begin
              Name := 'dynedt' + sAFieldName; // Name在后面赋值Name会默认显示在Text
              Alignment := alCenterCenter; // use cxGroupBox
              // iLabelTop := iLabelTop + 30;
              Top := iComponentTop;
              Height := 22;
              Caption := '';
              DataBinding.DataField := sAFieldName;
              DataBinding.DataSource := MainDataSource;
              Parent := PanelData;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
              sParameters := qyDynDtl.FieldByName('DT_LookupParams').AsString;
              slValue := TStringList.Create;
              sValue := '';
              m := Pos('{', sParameters);
              if (m > 0) then
              begin
                sParameters := Trim(Copy(sParameters, m + 1, Length(sParameters) - m));
                m := Pos(':', sParameters);
                while (m > 0) do
                begin
                  sValue := Trim(Copy(sParameters, 1, m - 1));
                  sTmp := '';
                  n := Pos(';', sParameters);
                  if (n > 0) then
                  begin
                    sTmp := Copy(sParameters, m + 1, n - m - 1);
                    with Properties.Items.Add do
                    begin
                      Caption := Trim(sTmp);
                      Value := sValue;
                      slValue.Add(sValue + '=' + Trim(sTmp));
                    end;
                    with TcxRadioGroupProperties(oADBColumn.Properties).Items.Add do
                    begin
                      Value := StrToFloatDef(sValue, 0);
                      Caption := Trim(sTmp);
                    end;
                    sParameters := Copy(sParameters, n + 1, Length(sParameters) - n);
                    m := Pos(':', sParameters);
                  end
                  else
                  begin
                    sTmp := Copy(sParameters, m + 1, Length(sParameters) - m - 1);
                    with Properties.Items.Add do
                    begin
                      Caption := Trim(sTmp);
                      Value := sValue;
                      slValue.Add(sValue + '=' + Trim(sTmp));
                    end;
                    with TcxRadioGroupProperties(oADBColumn.Properties).Items.Add do
                    begin
                      Value := StrToFloatDef(sValue, 0);
                      Caption := Trim(sTmp);
                    end;
                    break;
                  end;
                end;
              end;
              Properties.DefaultCaption := Properties.Items.Items[0].Caption;
              Properties.DefaultValue := Properties.Items.Items[0].Value;
              Properties.Columns := Properties.Items.Count;
              TcxRadioGroupProperties(oADBColumn.Properties).Columns := Properties.Items.Count;
            end;
          end
          // 字符形
          else if sDataType = 'S' then
          begin
            oAComponent := TcxDBTextEdit.Create(oForm); // TDBEdit.Create(oForm.PanelData);
            with TcxDBTextEdit(oAComponent) do
            begin
              Name := 'dynedt' + sAFieldName; // Name在后面赋值Name会默认显示在Text
              DataBinding.DataField := sAFieldName;
              DataBinding.DataSource := MainDataSource;
              Parent := PanelData;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          // 日期形，日期时间形
          else if (sDataType = 'D') or (sDataType = 'DT') then
          begin
            oAComponent := TcxDBDateEdit.Create(oForm);
            with TcxDBDateEdit(oAComponent) do
            begin
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              Parent := PanelData;
              if (qyDynDtl.FieldByName('DT_DataType').AsString = 'D') then
              begin
                Properties.Kind := ckDate;
                Properties.SaveTime := False;
              end
              else if (qyDynDtl.FieldByName('DT_DataType').AsString = 'DT') then
              begin
                Properties.Kind := ckDateTime;
                Properties.SaveTime := False;
              end;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          else if (sDataType = 'T') then
          begin
            oAComponent := TcxDBTimeEdit.Create(oForm);
            with TcxDBTimeEdit(oAComponent) do
            begin
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              Parent := PanelData;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          else if (sDataType = 'I') then
          begin
            oAComponent := TcxDBSpinEdit.Create(oForm);
            with TcxDBSpinEdit(oAComponent) do
            begin
              Parent := PanelData;
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              TcxSpinEditProperties(Properties).DisplayFormat := '#0';
              TcxSpinEditProperties(Properties).EditFormat := '#0';
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          // TcxDBCurrencyEdit   M 金额型 P单价 N数量 I整型
          else if (sDataType = 'M') or (sDataType = 'N') or (sDataType = 'P') then
          begin
            oAComponent := TcxDBCurrencyEdit.Create(oForm);
            with TcxDBCurrencyEdit(oAComponent) do
            begin
              Parent := PanelData;
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              if (sDataType = 'M') then
              begin
                TcxCurrencyEditProperties(Properties).DisplayFormat := '##,###,###0.00';
                TcxCurrencyEditProperties(Properties).EditFormat := '##,###,###0.00';
                TcxCurrencyEditProperties(Properties).DecimalPlaces := 2;
              end
              else if (sDataType = 'P') then
              begin
                TcxCurrencyEditProperties(Properties).DisplayFormat := '#0.########';
                TcxCurrencyEditProperties(Properties).EditFormat := '#0.########';
                TcxCurrencyEditProperties(Properties).DecimalPlaces := 8;
              end
              else if (sDataType = 'N') then
              begin
                TcxCurrencyEditProperties(Properties).DisplayFormat := '##,###,###0.0000';
                TcxCurrencyEditProperties(Properties).EditFormat := '##,###,###0.0000';
                TcxCurrencyEditProperties(Properties).DecimalPlaces := 4;
              end;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          else if (sDataType = 'MC') or (sDataType = 'NC') or (sDataType = 'PC') or (sDataType = 'IC')
          then
          begin
            oAComponent := TcxDBCalcEdit.Create(oForm);
            with TcxDBCalcEdit(oAComponent) do
            begin
              Parent := PanelData;
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              if (sDataType = 'MC') then
              begin
                TcxCalcEditProperties(Properties).DisplayFormat := '##,###,###0.00';
                TcxCalcEditProperties(Properties).EditFormat := '##,###,###0.00';
              end
              else if (sDataType = 'PC') then
              begin
                TcxCalcEditProperties(Properties).DisplayFormat := '#0.########';
                TcxCalcEditProperties(Properties).EditFormat := '#0.########';
              end
              else if (sDataType = 'NC') then
              begin
                TcxCalcEditProperties(Properties).DisplayFormat := '##,###,###0.0000';
                TcxCalcEditProperties(Properties).EditFormat := '##,###,###0.0000';
              end
              else if (sDataType = 'IC') then
              begin
                TcxCalcEditProperties(Properties).DisplayFormat := '#0';
                TcxCalcEditProperties(Properties).EditFormat := '#0';
              end;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end
          // 布尓形
          else if (sDataType = 'B') then
          begin
            oAComponent := TcxDBCheckBox.Create(oForm);
            with TcxDBCheckBox(oAComponent) do
            begin
              Name := 'dynedt' + sAFieldName;
              Caption := '';
              DataBinding.DataSource := MainDataSource;
              DataBinding.DataField := sAFieldName;
              Parent := PanelData;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
              Properties.ImmediatePost := True;
              Properties.NullStyle := nssUnchecked;
              Properties.Alignment := taLeftJustify;
            end;
          end
          else // 无匹配类型使用字符型
          begin
            oAComponent := TcxDBTextEdit.Create(oForm); // TDBEdit.Create(oForm.PanelData);
            with TcxDBTextEdit(oAComponent) do
            begin
              Name := 'dynedt' + sAFieldName; // Name在后面赋值Name会默认显示在Text
              DataBinding.DataField := sAFieldName;
              DataBinding.DataSource := MainDataSource;
              Parent := PanelData;
              Style.Shadow := qyDynDtl.FieldByName('DT_Shadow').AsBoolean;
            end;
          end;
          oAComponent.Name := 'dynedt' + sAFieldName;
          SetOrdProp(oAComponent, 'Width', iComponentWidth);
          SetOrdProp(oAComponent, 'Top', iComponentTop);
          SetOrdProp(oAComponent, 'Left', iComponentLeft);
          if qyDynDtl.FieldByName('Dt_IsReadOnly').AsBoolean then
          begin
            oALabel.Enabled := False;
            TWinControl(oAComponent).Enabled := False;
            AddFixCtrl(TWinControl(oAComponent));
            AddFixCtrl(sAFieldName);
          end;
          if qyDynDtl.FieldByName('Dt_IsNotNull').AsBoolean or qyDynDtl.FieldByName('Dt_MustNotNull').AsBoolean
          then
          begin
            oALabel.Style.Font.Color := clBlue;
          end;
          if GetPropInfo(oAComponent, 'DataField') <> nil then
            SetStrProp(oAComponent, 'DataField', sAFieldName);
          if GetPropInfo(oAComponent, 'DataSource') <> nil then
            SetObjectProp(oAComponent, 'DataSource', MainDataSource);
          New(oControlProperty);
          oControlProperty^.FieldName := sAFieldName;
          oControlProperty^.FieldCaption := qyDynDtl.FieldByName('DT_FieldCaption').AsString;
          oControlProperty^.ISReadOnly := qyDynDtl.FieldByName('DT_ISReadOnly').AsBoolean;
          oControlProperty^.ISLookupShowField := qyDynDtl.FieldByName('DT_ISLookupShow').AsBoolean;
          oControlProperty^.ISNotNULL := qyDynDtl.FieldByName('DT_ISNotNull').AsBoolean or
            qyDynDtl.FieldByName('DT_MustNotNull').AsBoolean;
          // if (oAComponent is TcxCustomLookupComboBox) then
          // TcxCustomLookupComboBox(oAComponent).Properties.ReadOnly := True
          // else
          // SetBooleanPropertyIfExists(oAComponent, 'ReadOnly', True);

          oAComponent.Tag := integer(oControlProperty);
          Inc(iRow);
        end;
        qyDynDtl.Next;
      end;
    except
      on E:Exception do
      begin
        ShowErrorBox(Format('创建动态布局字段%s[%s]控件错误，错误信息：%s'
          , [sAFieldCaption, sAFieldName, E.Message]));
      end;
    end;
    PanelData.Height := iLabelTop + 25;
    if grdDtl.Visible then
    begin
      grdDtl.Top := iLabelTop + 10;
      // PanelData.Height := iLabelTop + 150;
      PanelData.Height := Trunc(Height / 2);
    end;
    qyDataSet.Close;
    FreeAndNil(qyDataSet);
    SetViewColumnWidth(MainDataGridDBTableView);
    SetLayout;
    oForm.MainDataSource.DataSet.AfterScroll := oForm.MainDataSetUAfterScroll;
    oForm.MainDataSetUAfterScroll(oForm.MainDataSource.DataSet);
    if not bShowModal then
      Show;
    Result := True;
  end;
end;

function GetLookupTableIndex(ALookupID, AParam: string): integer;
var
  i: integer;
  sLookup: string;
begin
  Result := -1;
  for i := 0 to MainDM.LookupDS.Count - 1 do
  begin
    sLookup := LowerCase(PLookupTableInfo(TDataSource(MainDM.LookupDS[i]).DataSet.Tag)^.LookupNO);
    // sParam := LowerCase(PLookupTableInfo(TDataSource(MainDM.LookupDS[i]).DataSet.Tag)^.Params);
    if (sLookup = LowerCase(ALookupID)) then // and (sParam = LowerCase(AParam)) then
    begin
      Result := i;
      exit;
    end;
  end;
end;

function GetLookupTableIndex(ALookupCombox: TObject): integer;
var
  i: integer;
begin
  Result := -1;
  for i := 0 to MainDM.LookupDS.Count - 1 do
  begin
    if (ALookupCombox is TcxDBLookupComboBox) and
      (TcxDBLookupComboBox(ALookupCombox).Tag = TDataSource(MainDM.LookupDS[i]).Tag) then
    begin
      Result := i;
      exit;
    end;
    if (ALookupCombox is TcxDBComboBox) and
      (TcxDBComboBox(ALookupCombox).Tag = TDataSource(MainDM.LookupDS[i]).Tag)
    then
    begin
      Result := i;
      exit;
    end;
    if (ALookupCombox is TcxGridDBColumn) and
      (TcxGridDBColumn(ALookupCombox).Tag = TDataSource(MainDM.LookupDS[i]).Tag)
    then
    begin
      Result := i;
      exit;
    end;
  end;
end;

// 获取下拉数据源

function GetLookupDataSource(ALookupID, AParam: string): TDataSource;
var
  iIndex: integer;
begin
  Result := nil;
  iIndex := GetLookupTableIndex(ALookupID, AParam);
  if iIndex = -1 then
  begin
    ShowInfomationBox('加载关联数据集' + ALookupID + '[' + AParam + ']时找不到相应的数据表');
    exit;
  end;
  Result := MainDM.LookupDS[iIndex];
  if (Result <> nil) and not Result.DataSet.Active then
  begin
    if (LowerCase(ALookupID) = 'td_product') and (GetSysParamValue('bomsecret') = '1') and
      (not MainDM.GetModuleRight(1, 7005, rtExec)) then // 配方保密
    begin
      TUniQuery(Result.DataSet).SQL.Text :=
        'select code,case when kind<3 then code else name end as name,spec,* from td_product order by code';
    end;
    Result.DataSet.Open;
  end;
end;

function GetLokupDsNotOpen(ALookupID, AParam: string): TDataSource;
var
  iIndex: integer;
begin
  Result := nil;
  iIndex := GetLookupTableIndex(ALookupID, AParam);
  if iIndex = -1 then
  begin
    ShowInfomationBox('加载关联数据集' + ALookupID + '[' + AParam + ']时找不到相应的数据表');
    exit;
  end;
  Result := MainDM.LookupDS[iIndex];
end;

// 获取下拉数据集

function GetLookupTable(ALookupID, AParam: string): TDataSet;
var
  DataSource: TDataSource;
begin
  Result := nil;
  DataSource := GetLookupDataSource(ALookupID, AParam);
  if (DataSource <> nil) then
    Result := DataSource.DataSet;
end;

function GetLokupTbNotOpen(ALookupID, AParam: string): TDataSet;
var
  DataSource: TDataSource;
begin
  Result := nil;
  DataSource := GetLokupDsNotOpen(ALookupID, AParam);
  if (DataSource <> nil) then
    Result := DataSource.DataSet;
end;

{$HINTS OFF}


procedure SetLookupCombox(ALookupCombox: TObject; ALookupID, ALookupParam: string;
  AShowStyle: TLookupComboxShowStyle;
  ShowField: string = '');
var
  iLookupIndex: integer;
  FOnKeyUp: TcxDBLookupOnKeyUp;
  FLookupID: string;
begin
  iLookupIndex := -1;
  // FLookupID为空为控件已设置好数据连接，这里不再设置 Pegasus 2010-07-20
  FLookupID := '';
  if ALookupID <> '' then // 动态窗体使用
  begin
    iLookupIndex := GetLookupTableIndex(ALookupID, ALookupParam);
    FLookupID := ALookupID;
  end
  else if TcxDBLookupComboBox(ALookupCombox).Tag > 0 then // 静态窗体使用
  begin
    iLookupIndex := GetLookupTableIndex(ALookupCombox);
    if iLookupIndex >= 0 then
      FLookupID := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet.Tag)
        ^.LookupNO;
  end;
  if (iLookupIndex < 0) and ((ALookupID <> '') or (TcxDBLookupComboBox(ALookupCombox).Tag > 0)) then
  begin
    ShowInfomationBox('没有找到关联数据表,关联编号=' + ALookupID + '，参数=' + ALookupParam);
    exit;
  end;
  FOnKeyUp := TcxDBLookupOnKeyUp.Create(TcxCustomLookupComboBox(ALookupCombox), FLookupID,
    AShowStyle, ShowField);
  TDBLookupComboBox(ALookupCombox).Tag := integer(FOnKeyUp);
  if (ALookupParam <> '') and (Pos('=', ALookupParam) > 0) then
    FOnKeyUp.DropDownFilter := ALookupParam;
end;
{$HINTS ON}


procedure SetLookupcxGridColumn(ParentForm: TfrmBaseForm; ADBColumn: TObject;
  ALookupID, ALookupParam: string;
  ShowField: string = ''); // 设置关联CXGRID属性，从全局关联数组里找
var
  iLookupIndex: integer;
  FLookupID: string;
  FOnChange: TcxDBColumnOnChange;
begin
  iLookupIndex := -1;
  // FLookupID为空为控件已设置好数据连接，这里不再设置 Pegasus 2010-07-20
  FLookupID := '';
  if ALookupID <> '' then // 动态窗体使用
  begin
    iLookupIndex := GetLookupTableIndex(ALookupID, ALookupParam);
    FLookupID := ALookupID;
  end
  else if TcxGridDBColumn(ADBColumn).Tag > 0 then // 静态窗体使用
  begin
    iLookupIndex := GetLookupTableIndex(ADBColumn);
    if iLookupIndex >= 0 then
      FLookupID := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet.Tag)
        ^.LookupNO;
  end;
  if (iLookupIndex < 0) and ((ALookupID <> '') or (TcxGridDBColumn(ADBColumn).Tag > 0)) then
  begin
    ShowInfomationBox('没有找到关联数据表,关联编号=' + ALookupID + '，参数=' + ALookupParam);
    exit;
  end;
  // 按键设置 Pegasus 2010-07-20  FLookupID为空为控件已设置好数据连接，不处理新增，修改... Pegasus 2010-07-20
  FOnChange := TcxDBColumnOnChange.Create(ParentForm, TcxGridDBColumn(ADBColumn), FLookupID,
    ShowField);
  TcxGridDBColumn(ADBColumn).Tag := integer(FOnChange);
  if (ALookupParam <> '') and (Pos('=', ALookupParam) > 0) then
    FOnChange.DropDownFilter := ALookupParam;
end;

{$HINTS OFF}


procedure SetDateEdit(ADBDateEdit: TObject);
var
  FOnKeyDown: TcxDBDateEditKeyDown;
begin
  if (ADBDateEdit is TcxDBDateEdit) or (ADBDateEdit is TcxDateEdit) then
    FOnKeyDown := TcxDBDateEditKeyDown.Create(TcxDBDateEditKeyDown(ADBDateEdit));
end;
{$HINTS ON}
// 设置关联CXGRID属性，从全局关联数组里找

procedure SetComboBoxcxGridColumn(ParentForm: TfrmBaseForm; ADBColumn: TObject;
  ALookupID, ALookupParam: string;
  TableName: string = '');
var
  FOnChange: TcxDBColumnComBoxOnChange;
begin
  // 按键设置 Pegasus 2010-07-20  FLookupID为空为控件已设置好数据连接，不处理新增，修改... Pegasus 2010-07-20
  FOnChange := TcxDBColumnComBoxOnChange.Create(ParentForm, TcxGridDBColumn(ADBColumn), ALookupID,
    ALookupParam,
    TableName);
  TcxGridDBColumn(ADBColumn).Tag := integer(FOnChange);
end;

procedure SetComboBox(Sender: TcxDBComboBox; LookupID, Param: string; TableName: string = '');
var
  oLookupDataSet: TDataSet;
  iLookupIndex: integer;
  sFieldName, sLookupID, sAddField, sKeyField: string;
begin
  sFieldName := Sender.DataBinding.DataField;
  sLookupID := LowerCase(LookupID);
  if sLookupID = '' then
  begin
    iLookupIndex := GetLookupTableIndex(Sender);
    if iLookupIndex >= 0 then
      sLookupID := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet.Tag)
        ^.LookupNO;
    oLookupDataSet := GetLookupTable(sLookupID, '');
  end
  else
  begin
    if (Pos('ts_kind', sLookupID) > 0) and (sLookupID <> 'ts_kind') then
    begin
      Param := Copy(sLookupID, 8, Length(sLookupID) - 7);
      sLookupID := 'ts_kind';
    end;
    oLookupDataSet := GetLookupTable(sLookupID, '');
  end;
  if Param = '' then
    Param := Sender.Hint;
  sAddField := PLookupTableInfo(oLookupDataSet.Tag)^.ShowField;
  sKeyField := PLookupTableInfo(oLookupDataSet.Tag)^.KeyField;
  if (sLookupID = 'ts_custom') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'tablename=' + QuotedStr(TableName) + ' and fieldname=' +
      QuotedStr(sFieldName);
    oLookupDataSet.Filtered := True;
  end
  else if (sLookupID = 'ts_kind') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'kind=' + Param;
    oLookupDataSet.Filtered := True;
    Sender.Tag := StrToInt(Param);
    Sender.Hint := '';

    if oLookupDataSet.FieldByName('iscustom').AsBoolean then
    begin
      Sender.Properties.DropDownListStyle := lsEditList;
    end
    else
      Sender.Properties.DropDownListStyle := lsFixedList;
  end;
  Sender.Properties.IncrementalSearch := False;
  Sender.Properties.ImmediatePost := True;
  // 此开关不能为True，否则输入中文会有乱码 D7不支持UniCode
  // Sender.Properties.IncrementalFiltering := False;
  oLookupDataSet.First;
  while not oLookupDataSet.Eof do
  begin
    Sender.Properties.Items.AddObject(oLookupDataSet.FieldByName(sAddField).AsString,
      TObject(oLookupDataSet.FieldByName(sKeyField).AsString));
    // .Add(oLookupDataSet.FieldByName(sAddField).AsString);
    oLookupDataSet.Next;
  end;
  oLookupDataSet.Filtered := False;
  oLookupDataSet.Filter := '';
  oLookupDataSet.First;
end;

procedure SetComBox(Sender: TcxComboBox; LookupID, Param: string; TableName: string = '');
var
  oLookupDataSet: TDataSet;
  iLookupIndex: integer;
  sFieldName, sLookupID, sAddField, sKeyField: string;
begin
  sLookupID := LowerCase(LookupID);
  if sLookupID = '' then
  begin
    iLookupIndex := GetLookupTableIndex(Sender);
    if iLookupIndex >= 0 then
      sLookupID := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet.Tag)
        ^.LookupNO;
    oLookupDataSet := GetLookupTable(sLookupID, '');
  end
  else
  begin
    if (Pos('ts_kind', sLookupID) > 0) and (sLookupID <> 'ts_kind') then
    begin
      Param := Copy(sLookupID, 8, Length(sLookupID) - 7);
      sLookupID := 'ts_kind';
    end;
    oLookupDataSet := GetLookupTable(sLookupID, '');
  end;
  if Param = '' then
    Param := Sender.Hint;
  sAddField := PLookupTableInfo(oLookupDataSet.Tag)^.ShowField;
  sKeyField := PLookupTableInfo(oLookupDataSet.Tag)^.KeyField;
  if (sLookupID = 'ts_custom') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'tablename=' + QuotedStr(TableName) + ' and fieldname=' +
      QuotedStr(sFieldName);
    oLookupDataSet.Filtered := True;
  end
  else if (sLookupID = 'ts_kind') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'kind=' + Param;
    oLookupDataSet.Filtered := True;
    Sender.Tag := StrToInt(Param);
    Sender.Hint := '';

    if oLookupDataSet.FieldByName('iscustom').AsBoolean then
    begin
      Sender.Properties.DropDownListStyle := lsEditList;
    end
    else
      Sender.Properties.DropDownListStyle := lsFixedList;
  end;
  // 此开关不能为True，否则输入中文会有乱码  D7不支持UniCode
  Sender.Properties.IncrementalSearch := False;
  Sender.Properties.ImmediatePost := True;
  oLookupDataSet.First;
  while not oLookupDataSet.Eof do
  begin
    Sender.Properties.Items.AddObject(oLookupDataSet.FieldByName(sAddField).AsString,
      TObject(oLookupDataSet.FieldByName(sKeyField).AsString));
    oLookupDataSet.Next;
  end;
  oLookupDataSet.Filtered := False;
  oLookupDataSet.Filter := '';
  oLookupDataSet.First;
end;

procedure SetComboBoxSQL(Sender: TcxComboBox; SQL: string; ClearItem: Boolean = True);
var
  DataSet: TUniQuery;
begin
  if ClearItem then
    Sender.Properties.Items.Clear;
  Sender.Properties.DropDownListStyle := lsFixedList;
  Sender.Properties.IncrementalSearch := False;
  Sender.Properties.ImmediatePost := True;
  DataSet := nil;
  GetADOQuery(DataSet, SQL);
  DataSet.First;
  while not DataSet.Eof do
  begin
    Sender.Properties.Items.AddObject(DataSet.FieldByName('name').AsString,
      TObject(DataSet.FieldByName('id').AsInteger));
    DataSet.Next;
  end;
  DataSet.Close;
  FreeAndNil(DataSet);
end;

function GetFocusFieldName(Sender: TcxGridDBTableView): string;
// var
// i: integer;
begin
  Result := '';
  if Sender = nil then
    exit;
  Result := LowerCase((Sender.Controller.FocusedColumn as TcxGridDBColumn).DataBinding.FieldName);
  // for i := 0 to Sender.ColumnCount - 1 do
  // begin
  // if Sender.Columns[i].Focused then
  // begin
  // Result := LowerCase(Sender.Columns[i].DataBinding.FieldName);
  // Break;
  // end;
  // end;
end;

function GetFocusFieldName(Sender: TcxGridDBBandedTableView): string;
begin
  Result := '';
  if Sender = nil then
    exit;
  if Sender.Controller.FocusedColumn = nil then
    exit;
  Result := LowerCase((Sender.Controller.FocusedColumn as TcxGridDBBandedColumn)
    .DataBinding.FieldName);
end;

function GetColWidth(Sender: TcxGridDBTableView; FieldName: string): integer; overload;
begin
  Result := 0;
  if (Sender.GetColumnByFieldName(FieldName) <> nil) and
    (Sender.GetColumnByFieldName(FieldName).Visible) then
    Result := Sender.GetColumnByFieldName(FieldName).Width;
end;

function GetColWidth(Sender: TcxGridDBBandedTableView; FieldName: string): integer; overload;
begin
  Result := 0;
  if (Sender.GetColumnByFieldName(FieldName) <> nil) and
    (Sender.GetColumnByFieldName(FieldName).Visible) then
    Result := Sender.GetColumnByFieldName(FieldName).Width;
end;

function GetColVisible(Sender: TcxGridDBTableView; FieldName: string): Boolean; overload;
begin
  Result := False;
  if (Sender.GetColumnByFieldName(FieldName) <> nil) and
    (Sender.GetColumnByFieldName(FieldName).Visible) then
    Result := True;
end;

function GetColVisible(Sender: TcxGridDBBandedTableView; FieldName: string): Boolean; overload;
begin
  Result := False;
  if (Sender.GetColumnByFieldName(FieldName) <> nil) and
    (Sender.GetColumnByFieldName(FieldName).Visible) then
    Result := True;
end;

function GetColCaption(Sender: TcxGridDBTableView; FieldName: string): string; overload;
begin
  Result := '';
  if (Sender.GetColumnByFieldName(FieldName) <> nil) then
    Result := Sender.GetColumnByFieldName(FieldName).Caption;
  if Pos('***', Result) > 0 then
    Result := StringReplace(Result, '***', '', [rfReplaceAll]);
  if Pos('---', Result) > 0 then
    Result := StringReplace(Result, '---', '', [rfReplaceAll]);
end;

function GetColCaption(Sender: TcxGridDBBandedTableView; FieldName: string): string; overload;
begin
  Result := '';
  if (Sender.GetColumnByFieldName(FieldName) <> nil) then
    Result := Sender.GetColumnByFieldName(FieldName).Caption;
  if Pos('***', Result) > 0 then
    Result := StringReplace(Result, '***', '', [rfReplaceAll]);
  if Pos('---', Result) > 0 then
    Result := StringReplace(Result, '---', '', [rfReplaceAll]);
end;

function GetBandByCaption(Sender: TcxGridDBBandedTableView; Caption: string): TcxGridBand;
var
  i: integer;
begin
  Result := nil;
  for i := 0 to Sender.Bands.Count - 1 do
  begin
    if Sender.Bands.Items[i].Caption = Caption then
    begin
      Result := Sender.Bands.Items[i];
      break;
    end;
  end;
end;

function GetSelColRecText(Sender: TcxGridDBTableView; FullRecord: Boolean = False): string;
  overload;
var
  i, j, iRowIndex, iNew: integer;
begin
  Result := '';
  Assert(Sender.OptionsSelection.CellSelect, Sender.Name + '请设置单元格可选');
  // j := MainView.Controller.FocusedColumn.Index;
  for i := 0 to Sender.DataController.GetSelectedCount - 1 do
  begin
    iNew := 0;
    iRowIndex := Sender.DataController.GetSelectedRowIndex(i);
    for j := 0 to Sender.ColumnCount - 1 do
    begin
      if Sender.Columns[j].Selected or FullRecord then
      begin
        if iNew = 0 then
          Result := Result + Sender.DataController.displaytexts[iRowIndex, Sender.Columns[j].Index]
        else
          Result := Result + ' ' + Sender.DataController.displaytexts
            [iRowIndex, Sender.Columns[j].Index];
        // DtlGridDBTableView.VisibleColumns[j].Index];
        Inc(iNew);
      end;
    end;
    Result := Result + #13#10;
  end;
end;

function GetSelColRecText(Sender: TcxGridDBBandedTableView; FullRecord: Boolean = False)
  : string; overload;
var
  i, j, iRowIndex, iNew: integer;
begin
  Result := '';
  Assert(Sender.OptionsSelection.CellSelect, Sender.Name + '请设置单元格可选');
  // j := MainView.Controller.FocusedColumn.Index;
  for i := 0 to Sender.DataController.GetSelectedCount - 1 do
  begin
    iNew := 0;
    iRowIndex := Sender.DataController.GetSelectedRowIndex(i);
    for j := 0 to Sender.ColumnCount - 1 do // Sender.ColumnCount
    begin
      if Sender.Columns[j].Selected or FullRecord then
      begin
        if iNew = 0 then
          Result := Result + Sender.DataController.displaytexts[iRowIndex, Sender.Columns[j].Index]
        else
          Result := Result + ' ' + Sender.DataController.displaytexts
            [iRowIndex, Sender.Columns[j].Index];
        // DtlGridDBTableView.VisibleColumns[j].Index];
        Inc(iNew);
      end;
    end;
    Result := Result + #13#10;
  end;
end;

procedure SetSelColRecText(FieldName: string; Value: Variant; Sender: TcxGridDBBandedTableView);
var
  i, j: integer;
begin
  if Sender.DataController.GetSelectedCount = 1 then
    Exit;
  //屏蔽字典数据和固定公式的计算字段
  if (FieldName <> 'productid') and (FieldName <> 'unitid')
    and (Pos(FieldName, 'price') = 0) and (Pos(FieldName, 'quantity') = 0)
    and (Pos(FieldName, 'amount') = 0) and (Pos(FieldName, 'qty') = 0)
    and (Pos(FieldName, 'amt') = 0)
    and (FieldName <> 'unitname') and (FieldName <> 'p_code')
    and (FieldName <> 'p_name') and (FieldName <> 'p_spec')
    and (FieldName <> 'depotname') and (FieldName <> 'depotcode')
    and (FieldName <> 'depotid') and (FieldName <> 'traderid') then
    with Sender do
    begin
      Assert(OptionsSelection.CellSelect, Sender.Name + '请设置单元格可选');
      for i := 0 to DataController.GetSelectedCount - 1 do
      begin
        Controller.FocusedRow := Controller.SelectedRows[i];
        for j := 0 to ColumnCount - 1 do
          if Columns[j].Selected then // OnFieldChange触发必须SetFieldValNoChg，否则死循环
            SetFieldValNoChg(DataController.DataSource.DataSet, Columns[j].DataBinding.FieldName, Value);
      end;
    end;
end;

procedure AddFooterSum(Sender: TcxGridDBTableView; oADBColumn: TcxGridDBColumn;
  iFooterKind, iFooterSumType: integer;
  sDataType: string);
  function GetFormat(): string;
  begin
    case iFooterKind of
      1:
        Result := '合计:';
      2:
        Result := '平均:';
      3:
        Result := '统计:';
      4:
        Result := '最大:';
      5:
        Result := '最小:';
    else
      Result := '';
    end;
    if (sDataType = 'M') or (sDataType = 'MC') then
      Result := Result + '##,###,###0.00'
    else if (sDataType = 'I') or (sDataType = 'IC') then
      Result := Result + '#0'
    else if (sDataType = 'P') or (sDataType = 'PC') then
      Result := Result + '#0.########'
    else if (sDataType = 'N') or (sDataType = 'NC') then
      Result := Result + '#0.####'
    else if sDataType = 'D' then
      Result := Result + ''
    else if sDataType = 'DT' then
      Result := Result + ''
    else if sDataType = 'T' then
      Result := Result + ''
    else
      Result := '';
  end;

begin
  if (Sender = nil) or (oADBColumn = nil) then
    exit;
  if (iFooterSumType = 0) or (iFooterSumType = 2) then
  begin
    with TcxGridDBTableSummaryItem(Sender.DataController.Summary.FooterSummaryItems.Add) do
    begin
      Column := oADBColumn;
      case iFooterKind of
        1:
          Kind := skSum;
        2:
          Kind := skAverage;
        3:
          Kind := skCount;
        4:
          Kind := skMax;
        5:
          Kind := skMin;
      else
        Kind := skNone;
      end;
      Format := GetFormat;
      FieldName := oADBColumn.DataBinding.FieldName;
      DisplayText := oADBColumn.Caption;
    end;
  end;
  if (iFooterSumType = 1) or (iFooterSumType = 2) then
  begin
    with TcxGridDBTableSummaryItem(Sender.DataController.Summary.SummaryGroups.Items[0]
      .SummaryItems.Add) do
    begin
      Column := oADBColumn;
      Position := spFooter;
      case iFooterKind of
        1:
          Kind := skSum;
        2:
          Kind := skAverage;
        3:
          Kind := skCount;
        4:
          Kind := skMax;
        5:
          Kind := skMin;
      else
        Kind := skNone;
      end;
      Format := GetFormat;
      // FieldName := oADBColumn.DataBinding.FieldName;
      // DisplayText := oADBColumn.Caption;
    end
  end;
end;

procedure SetNextColumnFocused(Sender: TcxGridDBTableView; NextIndex: integer = 1); overload;
var
  i: integer;
begin
  // for i := 0 to Sender.VisibleColumnCount - 1 do
  // if Sender.VisibleColumns[i].Focused then
  // Break;
  i := Sender.Controller.FocusedColumnIndex;
  if NextIndex = 1 then
  begin
    if i = Sender.VisibleColumnCount - 1 then
    begin
      if Sender.Tag <= 1 then // 避免所有字段只读时死循环
      begin
        i := 0;
        Sender.Tag := Sender.Tag + 1; // 避免所有字段只读时死循环
      end;
    end
    else
      i := i + 1;
  end
  else if NextIndex = -1 then
  begin
    if i > 0 then
      i := i - 1;
  end;
  Sender.VisibleColumns[i].Focused := True;
end;

procedure SetNextColumnFocused(Sender: TcxGridDBBandedTableView; NextIndex: integer = 1); overload;
var
  i: integer;
begin
  // for i := 0 to Sender.VisibleColumnCount - 1 do
  // if Sender.VisibleColumns[i].Focused then
  // Break;
  i := Sender.Controller.FocusedColumnIndex;
  if NextIndex = 1 then
  begin
    if i = Sender.VisibleColumnCount - 1 then
    begin
      if Sender.Tag <= 1 then // 避免所有字段只读时死循环
      begin
        i := 0;
        Sender.Tag := Sender.Tag + 1; // 避免所有字段只读时死循环
      end;
    end
    else
      i := i + 1;
  end
  else if NextIndex = -1 then
  begin
    if i > 0 then
      i := i - 1;
  end;
  Sender.VisibleColumns[i].Focused := True;
end;

procedure AddViewColumn(Sender: TcxGridDBTableView; ACaption, AFieldName: string;
  AWidth: integer); overload;
begin
  with Sender.CreateColumn do
  begin
    Caption := ACaption;
    DataBinding.FieldName := AFieldName;
    Width := AWidth;
    MinWidth := Trunc(AWidth * 0.8);
  end;
end;

procedure AddViewColumn(Sender: TcxGridDBBandedTableView; ACaption, AFieldName: string;
  AWidth: integer); overload;
begin
  with Sender.CreateColumn do
  begin
    Caption := ACaption;
    DataBinding.FieldName := AFieldName;
    Width := AWidth;
    MinWidth := Trunc(AWidth * 0.8);
  end;
end;

procedure SetColWidth(Sender: TcxGridDBTableView; FieldName: string; iWidth: integer); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
  begin
    Sender.GetColumnByFieldName(FieldName).Width := iWidth;
    Sender.GetColumnByFieldName(FieldName).MinWidth := Trunc(iWidth * 0.8);
  end;
end;

procedure SetColWidth(Sender: TcxGridDBBandedTableView; FieldName: string;
  iWidth: integer); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
  begin
    Sender.GetColumnByFieldName(FieldName).Width := iWidth;
    Sender.GetColumnByFieldName(FieldName).MinWidth := Trunc(iWidth * 0.8);
  end;
end;

procedure SetColVisible(Sender: TcxGridDBTableView; FieldName: string; Visible: Boolean); overload;
// var
// AColumn: TcxCustomGridTableItem;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
  begin
    Sender.BeginUpdate;
    if Sender.GetColumnByFieldName(FieldName).Hideable then
      Sender.GetColumnByFieldName(FieldName).Hidden := not Visible;
    Sender.GetColumnByFieldName(FieldName).Visible := Visible;
    Sender.EndUpdate;
  end;
end;

procedure SetColVisible(Sender: TcxGridDBBandedTableView; FieldName: string;
  Visible: Boolean); overload;
var
  i, j: integer;
  bVisible: Boolean;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
  begin
    Sender.BeginUpdate;
    if Sender.GetColumnByFieldName(FieldName).Hideable then
      Sender.GetColumnByFieldName(FieldName).Hidden := not Visible;
    Sender.GetColumnByFieldName(FieldName).Visible := Visible;
    for i := 0 to Sender.ColumnCount - 1 do
    begin
      // Sender.VisibleColumns[i].VisibleIndex := i;
      Sender.Columns[i].Index := i;
    end;
    if Sender.OptionsView.BandHeaders then
      for i := 0 to Sender.Bands.Count - 1 do
      begin
        bVisible := False;
        for j := 0 to Sender.Bands.Items[i].ColumnCount - 1 do
        begin
          if Sender.Bands.Items[i].Columns[j].Visible then
          begin
            bVisible := True;
            break;
          end;
        end;
        Sender.Bands.Items[i].Visible := bVisible;
      end;
    Sender.EndUpdate;
  end;
end;

procedure SetColCaption(Sender: TcxGridDBTableView; FieldName, Caption: string); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    Sender.GetColumnByFieldName(FieldName).Caption := Caption;
end;

procedure SetColCaption(Sender: TcxGridDBBandedTableView; FieldName, Caption: string); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    Sender.GetColumnByFieldName(FieldName).Caption := Caption;
end;

procedure SetColBandsIndex(Sender: TcxGridDBBandedTableView; FieldName: string;
  iIndex: integer = 0); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    if Sender.GetColumnByFieldName(FieldName).Visible then
      if ((Sender.Bands.Count - 1) > iIndex) then
      begin
        if iIndex < 0 then
          Sender.GetColumnByFieldName(FieldName).Position.BandIndex := 0
        else
          Sender.GetColumnByFieldName(FieldName).Position.BandIndex := iIndex;
      end
      else
        Sender.GetColumnByFieldName(FieldName).Position.BandIndex := Sender.Bands.Count - 1;
end;

procedure SetColReadOnly(Sender: TcxGridDBTableView; FieldName: string; ReadOnly: Boolean);
  overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    Sender.GetColumnByFieldName(FieldName).Options.Editing := not ReadOnly;
end;

procedure SetColReadOnly(Sender: TcxGridDBBandedTableView; FieldName: string;
  ReadOnly: Boolean); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    Sender.GetColumnByFieldName(FieldName).Options.Editing := not ReadOnly;
end;

procedure SetColIndex(Sender: TcxGridDBTableView; FieldName: string; iIndex: integer = 0); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    if Sender.GetColumnByFieldName(FieldName).Visible then
      Sender.GetColumnByFieldName(FieldName).Index := iIndex;
end;

procedure SetColIndex(Sender: TcxGridDBBandedTableView; FieldName: string;
  iIndex: integer = 0); overload;
begin
  if Sender.GetColumnByFieldName(FieldName) <> nil then
    if Sender.GetColumnByFieldName(FieldName).Visible then
      Sender.GetColumnByFieldName(FieldName).Index := iIndex;
end;

procedure SetGridEnterNew(Sender: TcxGrid);
var
  tbView: TcxGridDBTableView;
  bdView: TcxGridDBBandedTableView;
begin
  tbView := nil;
  bdView := nil;
  if Sender.FocusedView is TcxGridDBTableView then
  begin
    tbView := Sender.FocusedView as TcxGridDBTableView;
    if (tbView = nil) or (tbView.DataController.DataSource = nil) then
      exit;
    if tbView.DataController.DataSource.DataSet.Active and tbView.DataController.DataSource.DataSet.IsEmpty then
      tbView.DataController.DataSource.DataSet.Append;
    tbView.Controller.EditingController.ShowEdit();
  end
  else if Sender.FocusedView is TcxGridDBBandedTableView then
  begin
    bdView := Sender.FocusedView as TcxGridDBBandedTableView;
    if (bdView = nil) or (bdView.DataController.DataSource = nil) then
      exit;
    if bdView.DataController.DataSource.DataSet.Active and bdView.DataController.DataSource.DataSet.IsEmpty then
      bdView.DataController.DataSource.DataSet.Append;
    bdView.Controller.EditingController.ShowEdit();
  end;
end;

procedure SetViewBand(Sender: TcxGridDBBandedTableView);
var
  i, j, iWidth, iGrdWidth: integer;
begin
  if Sender = nil then
    exit;
  Sender.OptionsView.ColumnAutoWidth := False;
  iGrdWidth := TcxGrid(TcxGridLevel(Sender.Level).GetParentComponent).Width;
  for i := 0 to Sender.Bands.Count - 1 do
  begin
    iWidth := 0;
    // Sender.Bands[i].Visible := Sender.Bands[i].ColumnCount > 0;
    for j := 0 to Sender.Bands[i].ColumnCount - 1 do
    begin
      //OutputDebugString(PWideChar(TcxGridDBBandedColumn(Sender.Bands[i].Columns[j]).DataBinding.FieldName));
      //WriteLog('Field:'+TcxGridDBBandedColumn(Sender.Bands[i].Columns[j]).DataBinding.FieldName
      //  +' Caption:'+TcxGridDBBandedColumn(Sender.Bands[i].Columns[j]).Caption);
      if (TcxGridDBBandedColumn(Sender.Bands[i].Columns[j]).DataBinding.FieldName <> '')
        and Sender.Bands[i].Columns[j].Visible then
      begin
        if Sender.Bands[i].Columns[j].Width > Sender.Bands[i].Columns[j].MinWidth then
          iWidth := iWidth + Sender.Bands[i].Columns[j].Width
        else
          iWidth := iWidth + Sender.Bands[i].Columns[j].MinWidth;
      end;
    end;
    if Sender.Bands[i] <> nil then
    begin
      Sender.Bands[i].Width := iWidth;
      Sender.Bands[i].Visible := iWidth > 0;
    end;
  end;
  Sender.OptionsView.BandHeaders := False;
  if Sender.Bands.VisibleCount > 2 then
  begin
    j := 0;
    for i := 0 to Sender.Bands.Count - 1 do
    begin
      if Sender.Bands[i].Visible then
      begin
        j := j + Sender.Bands[i].Width;
        if Trim(Sender.Bands[i].Caption) <> '' then
          Sender.OptionsView.BandHeaders := True;
      end;
    end;
    Sender.OptionsView.ColumnAutoWidth := j < iGrdWidth;
  end
  else
  begin
    iWidth := Sender.Bands[0].Width + Sender.Bands[1].Width;
    Sender.OptionsView.ColumnAutoWidth := iWidth < iGrdWidth;
    // if not g_bGridDrawFull then //单据画满表格和显示分组有兼容问题
    begin
      Sender.OptionsView.BandHeaders := (Trim(Sender.Bands[0].Caption) <> '') or
        (Trim(Sender.Bands[1].Caption) <> '');
    end;
  end;
  // Sender.OptionsView.ScrollBars := ssBoth;
end;

procedure SetViewColumnWidth(Sender: TcxGridDBTableView);
var
  i, iColumnWidth: integer;
begin
  if Sender = nil then
    exit;
  iColumnWidth := 0;
  for i := 0 to Sender.ColumnCount - 1 do
  begin
    if Sender.Columns[i].Width > Sender.Columns[i].MinWidth then
      iColumnWidth := iColumnWidth + Sender.Columns[i].Width
    else
      iColumnWidth := iColumnWidth + Sender.Columns[i].MinWidth;
  end;
  if iColumnWidth < TcxGrid(TcxGridLevel(Sender.Level).GetParentComponent).Width then
    Sender.OptionsView.ColumnAutoWidth := True;
end;

procedure SetViewColumn(Sender: TcxGridDBTableView; DataSet: TDataSet; BType, UserID: integer;
  Choice: Boolean = False);
var
  iLayoutid, Index: integer;
  oADBColumn: TcxGridDBColumn;
  FLookupID, FLookupParams: string;
  bShow: Boolean;
  DynField: TField;
  qryDynDtl: TUniQuery;
begin
  iLayoutid := GetUserLayOut(BType, UserID);
  GetADOQuery(qryDynDtl, 'select * from ts_DynamicEditFormDtl ' + ' where dt_editformid=' +
    IntToStr(BType) +
    ' and dt_layoutid=' + IntToStr(iLayoutid) +
    ' and dt_findcolumnshow=1 order by dt_fieldtype,dt_order');
  if qryDynDtl.IsEmpty then
    exit;
  Sender.BeginUpdate;
  Sender.ClearItems;
  if DataSet.FindField('choice') <> nil then
  begin
    oADBColumn := Sender.CreateColumn;
    oADBColumn.DataBinding.FieldName := 'choice';
    oADBColumn.PropertiesClass := TcxCustomCheckBoxProperties;
    oADBColumn.Properties.ImmediatePost := True;
    oADBColumn.Visible := Choice;
    oADBColumn.Caption := '选择';
    oADBColumn.Width := 40;
    oADBColumn.MinWidth := Trunc(oADBColumn.Width * 0.8);
  end;
  // 求每个字段的数据类型
  FLookupID := '';
  FLookupParams := '';
  qryDynDtl.First;
  while not qryDynDtl.Eof do
  begin
    DynField := nil;
    bShow := qryDynDtl.FieldByName('DT_FindColumnShow').AsBoolean;
    FLookupID := qryDynDtl.FieldByName('DT_LookupID').AsString;
    FLookupParams := qryDynDtl.FieldByName('DT_LookupParams').AsString;
    if (BType > 5000) then
    begin
      DynField := DataSet.FindField(qryDynDtl.FieldByName('DT_FieldName').AsString);
    end
    else
    begin
      if (qryDynDtl.FieldByName('DT_FieldType').AsInteger in [1, 3]) then
        DynField := DataSet.FindField('m_' + qryDynDtl.FieldByName('DT_FieldName').AsString)
      else if (qryDynDtl.FieldByName('DT_FieldType').AsInteger in [2, 4]) then
        DynField := DataSet.FindField(qryDynDtl.FieldByName('DT_FieldName').AsString);
    end;
    if DynField = nil then
    begin
      qryDynDtl.Next;
      Continue;
    end;
    DynField.DisplayLabel := qryDynDtl.FieldByName('DT_FieldCaption').AsString;
    if (DynField.DisplayLabel <> DynField.FieldName) and bShow then
    begin
      oADBColumn := Sender.CreateColumn;
      oADBColumn.DataBinding.FieldName := LowerCase(DynField.FieldName);
      oADBColumn.Editing := False;
      oADBColumn.Options.Focusing := False;
      oADBColumn.Caption := qryDynDtl.FieldByName('DT_FieldCaption').AsString;
      oADBColumn.Width := qryDynDtl.FieldByName('DT_ColWidth').AsInteger;
      oADBColumn.MinWidth := Trunc(oADBColumn.Width * 0.8);
      if qryDynDtl.FieldByName('DT_FooterType').AsInteger > 0 then
        AddFooterSum(Sender, oADBColumn, qryDynDtl.FieldByName('DT_FooterType').AsInteger, 0,
          Trim(qryDynDtl.FieldByName('Dt_DataType').AsString));
    end;

    qryDynDtl.Next;
  end;
  // SetColVisible(Sender, 'm_bkind', not (BType >= btGathering) and (BType <= btCancel));
  // 出入库单选单，使用默认布局时 单价金额是否显示要看权限
  if (BType = btStoIO) and (iLayoutid = -1) then
  begin
    bShow := False;
    // if IsPurBill(FiSelBType) then
    // bShow := MainDM.GetModuleRight(2, 10005, rtExec);
    // if IsSaleBill(FiSelBType) then
    // bShow := MainDM.GetModuleRight(2, 10021, rtExec);
    SetColVisible(Sender, 'price', bShow);
    SetColVisible(Sender, 'bprice', bShow);
    SetColVisible(Sender, 'dcprice', bShow);
    SetColVisible(Sender, 'amount', bShow);
    SetColVisible(Sender, 'dcamount', bShow);
  end;

  index := 0;
  qryDynDtl.First;
  while not qryDynDtl.Eof do
  begin
    if Sender.GetColumnByFieldName(qryDynDtl.FieldByName('DT_FieldName').AsString) <> nil then
    begin
      Sender.GetColumnByFieldName(qryDynDtl.FieldByName('DT_FieldName').AsString).Index := index;
      Inc(index);
    end;
    qryDynDtl.Next;
  end;
  SetColIndex(Sender, 'amount');
  SetColIndex(Sender, 'price');
  SetColIndex(Sender, 'quantity');
  SetColIndex(Sender, 'unitname');
  SetColIndex(Sender, 'p_name');
  SetColIndex(Sender, 'p_code');
  SetColIndex(Sender, 'depotname');
  SetColIndex(Sender, 'depotcode');

  SetColIndex(Sender, 'm_quantity');
  SetColIndex(Sender, 'm_unitname');
  SetColIndex(Sender, 'm_p_name');
  SetColIndex(Sender, 'm_p_code');

  SetColIndex(Sender, 'm_t_name');
  SetColIndex(Sender, 'm_bkind');
  SetColIndex(Sender, 'm_bcode');
  SetColIndex(Sender, 'm_bdate');
  SetColIndex(Sender, 'choice');
  Sender.Columns[0].Selected := True;
  Sender.EndUpdate;
end;

procedure DrawFullGrid(Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
  AViewInfo: TcxGridTableDataCellViewInfo;
  var ADone: Boolean);
var
  i: integer;
  lMaxRow: integer;
  Rect1: TRect; // ABorder: TcxBorder;
begin
  if not g_bGridDrawFull then
    exit;
  { ********此方法有缺点
    1 TcxCustomGridTableView无数据时不会执行DrawCell，所以就不会画格子，需要新增一条空记录
    2 TcxGridDBBandedTableView有固定列时无法执行此方法
    ************* }
  // ACanvas.FillRect(AViewInfo.Bounds);
  // ACanvas.Canvas.FillRect(AViewInfo.Bounds);
  // ACanvas.DrawText(AViewInfo.GridRecord.DisplayTexts[AViewInfo.Item.Index], AViewInfo.Bounds, cxAlignHCenter);
  // 计算最多画多少行
  lMaxRow := AViewInfo.GridViewInfo.NoDataInfoTextAreaBounds.Bottom -
    AViewInfo.GridViewInfo.NoDataInfoTextAreaBounds.Top mod AViewInfo.Bounds.Bottom -
    AViewInfo.Bounds.Top + 2;
  if AViewInfo.GridRecord.Index = Sender.ViewData.RecordCount - 1 then
  begin
    for i := Sender.ViewData.RecordCount + 1 to lMaxRow do
    begin
      // 画表格  计算表格坐标        //AViewInfo.ClientBounds
      // if Sender is TcxGridDBTableView then
      // begin
      Rect1.Top := AViewInfo.Bounds.Top - 1 + (AViewInfo.Bounds.Bottom - AViewInfo.Bounds.Top) *
        (i - Sender.ViewData.RecordCount);
      Rect1.Bottom := AViewInfo.Bounds.Bottom + (AViewInfo.Bounds.Bottom - AViewInfo.Bounds.Top) *
        (i - Sender.ViewData.RecordCount);
      Rect1.Left := AViewInfo.Bounds.Left - 1;
      Rect1.Right := AViewInfo.Bounds.Right + 1;
      // end
      // else if Sender is TcxGridDBBandedTableView then
      // begin
      // Rect1.Top := AViewInfo.Bounds.Top - 1 + (AViewInfo.Bounds.Bottom - AViewInfo.Bounds.Top) * (i - Sender.ViewData.RecordCount);
      // Rect1.Bottom := AViewInfo.Bounds.Bottom + (AViewInfo.Bounds.Bottom - AViewInfo.Bounds.Top) * (i - Sender.ViewData.RecordCount);
      // Rect1.Left := 1;
      // Rect1.Right := Sender.Site.Width;
      // end;
      if Sender.Styles.Content <> nil then
        ACanvas.Brush.Color := Sender.Styles.Content.Color
      else
        ACanvas.Brush.Color := clWhite;

      if (i mod 2) = 0 then
      begin
        if Sender.Styles.ContentOdd <> nil then
          ACanvas.Brush.Color := Sender.Styles.ContentOdd.Color
        else
          ACanvas.Brush.Color := MainDM.bsOdd.Color;
      end
      else
      begin
        if Sender.Styles.ContentEven <> nil then
          ACanvas.Brush.Color := Sender.Styles.ContentEven.Color
        else
          ACanvas.Brush.Color := MainDM.bsEven.Color;
      end;

      // ACanvas.Brush.Color := AViewInfo.BorderColor[ABorder];//AViewInfo.Style.BorderColor;   //clSilver
      // ACanvas.FrameRect(Rect1); //此方法会将最后一条记录填充颜色，而不是只画边框
      ACanvas.Canvas.FillRect(Rect1);
      ACanvas.Canvas.Rectangle(Rect1);
      // AViewInfo.GridView.Painter.ExcludeFromBackground(Rect1);
      Sender.Painter.ExcludeFromBackground(Rect1);
      // ADone := False; 会照成最后一行金额线消失
    end;
  end;
end;

procedure EmptyCellClick(Sender: TcxCustomGridTableView; Button: TMouseButton; DataSet: TDataSet;
  X, Y: integer);
// 左键点击了grid的空白新增记录
var
  AHitTest: TcxCustomGridHitTest;
begin
  if DataSet = nil then
    exit;
  if not(Sender is TcxCustomGridTableView) then
    exit;
  AHitTest := Sender.ViewInfo.GetHitTest(X, Y);
  if AHitTest = nil then
    exit;
  if (AHitTest.HitTestCode = 0) and (Button = mbLeft) and (Sender.OptionsData.Appending) and
    DataSet.Active and
    ((DataSet.State in [dsInactive, dsEdit, dsBrowse]) or DataSet.Modified) then // 左键点击了grid的空白
  begin
    if DataSet.FindField('productid') <> nil then
    begin
      // 未输入货品不能新增  排除凭证录入
      if (DataSet.FindField('voucherid') = nil) then
        if not DataSet.IsEmpty and (DataSet.FieldByName('productid').AsInteger = 0) then
          exit;
    end;
    Sender.DataController.BeginUpdate;
    if Sender.DataController.EditState <> [] then // if DataSet.State in [dsEdit, dsInsert] then
      Sender.DataController.Post; // DataSet.Post;
    Sender.DataController.Append; // DataSet.Append;//会报错RecordIndex out of range.
    Sender.DataController.EndUpdate;
  end;
end;

procedure SetViewToExcelColumn(Sender: TcxGrid; xlsReport: TxlsImExport);
var
  // 导出cxGrid导出Excel后设置数值型字段的字符格式，去除金额符号
  i, iCol: integer;
  sOldCurrStr: string;
begin
  if Sender.ActiveView is TcxGridDBBandedTableView then
  begin
    { 14.2.2版本有此问题，15.2.2不在需要此处理
      // 删除固定列的空栏
      if MainView.Bands[0].FixedKind = fkLeft then
      begin
      j := 0;
      for i := 0 to MainView.Bands[0].ColumnCount - 1 do
      begin
      if MainView.Bands[0].Columns[i].Visible then
      Inc(j);
      end;
      if (j > 0) then
      xlsReport.DropColumn(j + 1);
      end;
    }
    for iCol := 0 to (Sender.ActiveView as TcxGridDBBandedTableView).VisibleColumnCount - 1 do
    begin
      sOldCurrStr := '';
      i := iCol + 1;
      // 空出固定列
      if ((Sender.ActiveView as TcxGridDBBandedTableView).Bands.Count > 0) and
        ((Sender.ActiveView as TcxGridDBBandedTableView).Bands.Items[0].ColumnCount > 1) and
        (iCol > (Sender.ActiveView as TcxGridDBBandedTableView).Bands.Items[0].ColumnCount) then
        i := i + 1;
      if (Sender.ActiveView as TcxGridDBBandedTableView).VisibleColumns[iCol].Properties is TcxCurrencyEditProperties
      then
      begin
        sOldCurrStr := TcxCurrencyEditProperties((Sender.ActiveView as TcxGridDBBandedTableView)
          .VisibleColumns[iCol]
          .Properties).DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0.########';
      end
      else if (Sender.ActiveView as TcxGridDBBandedTableView).VisibleColumns[iCol].Properties is TcxCalcEditProperties
      then
      begin
        sOldCurrStr := TcxCalcEditProperties((Sender.ActiveView as TcxGridDBBandedTableView)
          .VisibleColumns[iCol]
          .Properties).DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0.########';
      end
      else if (Sender.ActiveView as TcxGridDBBandedTableView).VisibleColumns[iCol].Properties is TcxSpinEditProperties
      then
      begin
        sOldCurrStr := TcxSpinEditProperties((Sender.ActiveView as TcxGridDBBandedTableView)
          .VisibleColumns[iCol]
          .Properties).DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0';
      end;
      if sOldCurrStr <> '' then
        xlsReport.SetColumnFormat(i, sOldCurrStr);
    end;
  end
  else if Sender.ActiveView is TcxGridDBTableView then
  begin
    for iCol := 0 to (Sender.ActiveView as TcxGridDBTableView).VisibleColumnCount - 1 do
    begin
      sOldCurrStr := '';
      i := iCol + 1;
      if (Sender.ActiveView as TcxGridDBTableView).VisibleColumns[iCol].Properties is TcxCurrencyEditProperties
      then
      begin
        sOldCurrStr := TcxCurrencyEditProperties((Sender.ActiveView as TcxGridDBTableView)
          .VisibleColumns[iCol]
          .Properties).DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0.########';
      end
      else if (Sender.ActiveView as TcxGridDBTableView).VisibleColumns[iCol].Properties is TcxCalcEditProperties
      then
      begin
        sOldCurrStr := TcxCalcEditProperties((Sender.ActiveView as TcxGridDBTableView)
          .VisibleColumns[iCol].Properties)
          .DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0.########';
      end
      else if (Sender.ActiveView as TcxGridDBTableView).VisibleColumns[iCol].Properties is TcxSpinEditProperties
      then
      begin
        sOldCurrStr := TcxSpinEditProperties((Sender.ActiveView as TcxGridDBTableView)
          .VisibleColumns[iCol].Properties)
          .DisplayFormat;
        if sOldCurrStr = '' then
          sOldCurrStr := '#,##0';
      end;
      // 为设置DisplayFormat TcxGrid会默认加上货币符号
      if Pos('$,', sOldCurrStr) > 0 then // ￥€＄     ¥
        sOldCurrStr := ReplaceStr(sOldCurrStr, '$,', '');
      if Pos('¥,', sOldCurrStr) > 0 then // ￥€＄  ¥
        sOldCurrStr := ReplaceStr(sOldCurrStr, '¥,', '');
      if sOldCurrStr <> '' then
        xlsReport.SetColumnFormat(i, sOldCurrStr);
    end;
  end;
end;

procedure GridToExcel(var FileName: string; Sender: TcxGrid; SetNull: Boolean = True);
var
  ANullStrictConvert: Boolean;
begin
  ANullStrictConvert := NullStrictConvert;
  // AUseNativeFormat True 使用DataSet里的原始值 False 使用DislpayText
  // AUseNativeFormat True是会将Lookup字段的ID字段值导出，需要AddViewEvent处理cxgrid5.0时此代码正常，2014.2.2时代码不工作
  // AUseNativeFormat False时全是字符，后续需要将数字字段设置显示格式及转为数值型
  // ExportGridToXLSX(sName, MainDBGrid);  Office 2007以上版本

  try
    // NullStrictConvert := False;关闭Delphi的Variant的Null值转换检查
    // 也可以设置please deactivate the "Stop on Delphi exceptions" option (Tools-->Debugger Options...--> Language Exceptions).
    if SetNull then
      NullStrictConvert := False;

    // 2007
    if g_sExcelVer = '2007' then
    begin
      if Pos('.xlsx', FileName) = 0 then
        FileName := FileName + '.xlsx';
      ExportGridToXLSX(FileName, Sender, True, True, True);
    end
    else
    begin
      // 2003
      if Pos('.xls', FileName) <= 0 then
        FileName := FileName + '.xls';
      ExportGridToExcel(FileName, Sender, True, True, True);
    end;
  finally
    if SetNull then
      NullStrictConvert := ANullStrictConvert;
  end;
end;

procedure LoadTree(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; const p_TableName: string;
  p_PId: integer;
  const FilterSQL: string = ''; t_ADOQuery: TUniQuery = nil);
var
  t_RootNode: TTreeNode;
  t_TreeStruc: pTreeStruc;
  sSQL, sFilter, KeyField, CodeField, NameField: string;
begin
  sFilter := FilterSQL;
  KeyField := 'id';
  CodeField := 'code';
  NameField := 'name';
  if p_TableName = 'td_dept' then
  begin
    KeyField := 'ed_deptid';
    CodeField := 'ed_deptcd';
    NameField := 'ed_deptnm';
  end
  else if p_TableName = 'tfd_subject' then
  begin
    KeyField := 'subjectid';
    CodeField := 'subcode';
    NameField := 'subname';
  end;
  try
    {
      sSQL := 'select * from ' + p_TableName + ' where isnull(pid,0)=' + inttostr(p_PId) + FilterSQL + ' order by ' + CodeField;
      if p_TableName = 'tfd_subject' then
      sSQL := 'select * from ' + p_TableName + ' s where isnull(pid,0)=' + inttostr(p_PId) + FilterSQL + ' order by ' + CodeField;
      GetADOQuery(t_ADOQuery, sSQL);
    }

    if t_ADOQuery <> nil then
    begin
      t_ADOQuery.Filtered := False;
      t_ADOQuery.Filter := '(pid is null and 0=' + IntToStr(p_PId) + ') or pid=' + IntToStr(p_PId);
      t_ADOQuery.Filtered := True;
      sSQL := '';
    end
    else if t_ADOQuery = nil then
    begin
      sSQL := 'select * from ' + p_TableName + ' where 1=1' + FilterSQL + ' order by ' + CodeField;
      if p_TableName = 'tfd_subject' then
        sSQL := 'select * from ' + p_TableName + ' s where 1=1' + FilterSQL + ' order by ' +
          CodeField;
      GetADOQuery(t_ADOQuery, sSQL);
      t_ADOQuery.Filtered := False;
      t_ADOQuery.Filter := '(pid is null and 0=' + IntToStr(p_PId) + ') or pid=' + IntToStr(p_PId);
      t_ADOQuery.Filtered := True;
    end;

    if not t_ADOQuery.IsEmpty then
    begin
      with t_ADOQuery do
      begin
        First;
        while not Eof do
        begin
          New(t_TreeStruc);

          t_TreeStruc^.Id := FieldByName(KeyField).AsInteger;
          if p_TableName = 'tfd_subject' then
            t_TreeStruc^.Name := FieldByName(CodeField).AsString + ' ' +
              FieldByName(NameField).AsString
          else
            t_TreeStruc^.Name := FieldByName(NameField).AsString;
          t_TreeStruc^.Code := FieldByName(CodeField).AsString;
          t_TreeStruc^.LCode := FieldByName('SubCode').AsString;
          t_TreeStruc^.PId := FieldByName('PId').AsInteger;
          if FindField('ImgIndex') <> nil then
            t_TreeStruc^.ImgIndex := FieldByName('ImgIndex').AsInteger;

          t_RootNode := p_TreeView.Items.AddChildObject(p_TreeNode, t_TreeStruc^.Name, t_TreeStruc);
          if (p_TreeView.Images <> nil) and (p_TreeView.Images <> MainDM.imgList) then
          begin
            t_RootNode.ImageIndex := t_TreeStruc^.ImgIndex;
            t_RootNode.SelectedIndex := t_TreeStruc^.ImgIndex;
          end
          else // 加载完树后需要赋值
          begin
            if p_TreeView.Images = nil then // 加载完树后需要赋值
            begin
              p_TreeView.Images := MainDM.imgList;
            end;
            t_RootNode.ImageIndex := 38;
            t_RootNode.SelectedIndex := 41;
          end;
          LoadTree(p_TreeView, t_RootNode, p_TableName, t_TreeStruc^.Id, sFilter, t_ADOQuery);
          t_ADOQuery.Filtered := False;
          t_ADOQuery.Filter := '(pid is null and 0=' + IntToStr(p_PId) + ') or pid=' +
            IntToStr(p_PId);
          t_ADOQuery.Filtered := True;
          t_ADOQuery.Locate(KeyField, t_TreeStruc^.Id, []);
          Next;
        end;
      end;
    end;
  finally
    if p_TreeView.Items.Count > 0 then
      p_TreeView.Items[0].Expand(True);
    if sSQL <> '' then
    begin
      t_ADOQuery.Close;
      FreeAndNil(t_ADOQuery);
    end;
  end;
end;

procedure LoadTreeNoSubNode(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; p_TableName: string;
  p_PId: integer;
  KeyField: string = 'id';const FilterSQL: string = '');
var
  t_ADOQuery: TUniQuery;
  t_RootNode: TTreeNode;
  t_TreeStruc: pTreeStruc;
  CodeField, NameField: string;
begin
  CodeField := 'code';
  NameField := 'name';
  if KeyField = 'ep_postid' then
  begin
    CodeField := 'ep_postcd';
    NameField := 'ep_postnm';
  end
  else if KeyField = 'ui_userid' then
  begin
    CodeField := 'ui_usercode';
    NameField := 'ui_username';
  end
  else if KeyField = 'em_employeeid' then
  begin
    CodeField := 'em_code';
    NameField := 'em_name';
  end;
  try
    t_ADOQuery := nil;
    if Pos('select', p_TableName) > 0 then
      GetADOQuery(t_ADOQuery, p_TableName)
    else
      GetADOQuery(t_ADOQuery, 'select * from ' + p_TableName
        + ' where 1=1'+ FilterSQL
        + ' order by ' + KeyField);

    if not t_ADOQuery.IsEmpty then
    begin
      with t_ADOQuery do
      begin
        First;
        while not Eof do
        begin
          New(t_TreeStruc);

          t_TreeStruc^.Id := FieldByName(KeyField).AsInteger;
          t_TreeStruc^.Name := FieldByName(NameField).AsString;
          t_TreeStruc^.Code := FieldByName(CodeField).AsString;

          t_RootNode := p_TreeView.Items.AddChildObject(p_TreeNode, t_TreeStruc^.Name, t_TreeStruc);
          if (p_TreeView.Images <> nil) and (p_TreeView.Images <> MainDM.imgList) then
          begin
            t_RootNode.ImageIndex := t_TreeStruc^.ImgIndex;
            t_RootNode.SelectedIndex := t_TreeStruc^.ImgIndex;
          end
          else // 加载完树后需要赋值
          begin
            if p_TreeView.Images = nil then // 加载完树后需要赋值
            begin
              p_TreeView.Images := MainDM.imgList;
            end;
            t_RootNode.ImageIndex := 38;
            t_RootNode.SelectedIndex := 41;
          end;
          Next;
        end;
      end;
    end;
  finally
    if p_TreeView.Items.Count > 0 then
      p_TreeView.Items[0].Expand(True);
    if t_ADOQuery <> nil then
    begin
      if t_ADOQuery.Active then
        t_ADOQuery.Close;
      FreeAndNil(t_ADOQuery);
    end;
  end;
end;

procedure LoadTreeUserByDept(p_TreeView: TcxTreeView; p_TreeNode: TTreeNode; p_TableName: string;
  p_PId: integer);
var
  t_ADOQuery, qryUser: TUniQuery;
  t_RootNode, t_UserNode: TTreeNode;
  t_TreeStruc: pTreeStruc;
  KeyField, CodeField, NameField: string;
  procedure LoadUserByDept(DeptID: integer);
  begin
    qryUser := nil;
    if DeptID = TMP_ID then
      GetADOQuery(qryUser,
        'select * from ts_usersinfo where ui_deptid is null order by ui_usercode')
    else
      GetADOQuery(qryUser, 'select * from ts_usersinfo where ui_deptid=' + IntToStr(DeptID) +
        ' order by ui_usercode');
    if not qryUser.IsEmpty then
    begin
      with qryUser do
      begin
        First;
        while not Eof do
        begin
          New(t_TreeStruc);

          t_TreeStruc^.Id := FieldByName('ui_userid').AsInteger;
          t_TreeStruc^.CustomId := FieldByName('ui_userid').AsInteger;
          t_TreeStruc^.Name := FieldByName('ui_username').AsString;
          t_TreeStruc^.Code := FieldByName('ui_usercode').AsString;

          t_UserNode := p_TreeView.Items.AddChildObject(t_RootNode, t_TreeStruc^.Name, t_TreeStruc);
          if (p_TreeView.Images <> nil) and (p_TreeView.Images <> MainDM.imgList) then
          begin
            t_UserNode.ImageIndex := t_TreeStruc^.ImgIndex;
            t_UserNode.SelectedIndex := t_TreeStruc^.ImgIndex;
          end
          else // 加载完树后需要赋值
          begin
            if p_TreeView.Images = nil then // 加载完树后需要赋值
            begin
              p_TreeView.Images := MainDM.imgList;
            end;
            t_UserNode.ImageIndex := 38;
            t_UserNode.SelectedIndex := 41;
          end;
          Next;
        end;
      end;
    end;
    if qryUser.Active then
      qryUser.Close;
    FreeAndNil(qryUser);
  end;

begin
  KeyField := 'ed_deptid';
  CodeField := 'ed_deptcd';
  NameField := 'ed_deptnm';
  try
    if p_TreeNode = nil then
    begin
      New(t_TreeStruc);
      t_TreeStruc^.Id := TMP_ID - 1;
      t_TreeStruc^.CustomId := 1;
      t_TreeStruc^.Name := '全部';
      t_TreeStruc^.Code := '全部';
      with p_TreeView.Items.AddChildObject(nil, t_TreeStruc^.Name, t_TreeStruc) do
        SelectedIndex := 41;
      New(t_TreeStruc);
      t_TreeStruc^.Id := TMP_ID - 2;
      t_TreeStruc^.CustomId := 1;
      t_TreeStruc^.Name := '其他';
      t_TreeStruc^.Code := '其他';
      with p_TreeView.Items.AddChildObject(nil, t_TreeStruc^.Name, t_TreeStruc) do
        SelectedIndex := 41;
      New(t_TreeStruc);
      t_TreeStruc^.Id := TMP_ID;
      t_TreeStruc^.CustomId := TMP_ID;
      t_TreeStruc^.Name := '其他部门';
      t_TreeStruc^.Code := '其他部门';
      t_RootNode := p_TreeView.Items.AddChildObject(nil, t_TreeStruc^.Name, t_TreeStruc);
      t_RootNode.ImageIndex := 38;
      t_RootNode.SelectedIndex := 41;
      LoadUserByDept(TMP_ID);
    end;

    GetADOQuery(t_ADOQuery, 'select * from td_dept where isnull(pid,0)=' + IntToStr(p_PId) +
      ' order by ' + CodeField);

    if not t_ADOQuery.IsEmpty then
    begin
      with t_ADOQuery do
      begin
        First;
        while not Eof do
        begin
          New(t_TreeStruc);

          t_TreeStruc^.Id := FieldByName(KeyField).AsInteger;
          t_TreeStruc^.CustomId := 0;
          t_TreeStruc^.Name := FieldByName(NameField).AsString;
          t_TreeStruc^.Code := FieldByName(CodeField).AsString;
          t_TreeStruc^.LCode := FieldByName('SubCode').AsString;
          t_TreeStruc^.PId := FieldByName('PId').AsInteger;
          t_TreeStruc^.ImgIndex := FieldByName('ImgIndex').AsInteger;

          t_RootNode := p_TreeView.Items.AddChildObject(p_TreeNode, t_TreeStruc^.Name, t_TreeStruc);
          if (p_TreeView.Images <> nil) and (p_TreeView.Images <> MainDM.imgList) then
          begin
            t_RootNode.ImageIndex := t_TreeStruc^.ImgIndex;
            t_RootNode.SelectedIndex := t_TreeStruc^.ImgIndex;
          end
          else // 加载完树后需要赋值
          begin
            if p_TreeView.Images = nil then // 加载完树后需要赋值
            begin
              p_TreeView.Images := MainDM.imgList;
            end;
            t_RootNode.ImageIndex := 38;
            t_RootNode.SelectedIndex := 41;
          end;
          LoadUserByDept(FieldByName(KeyField).AsInteger);
          LoadTreeUserByDept(p_TreeView, t_RootNode, p_TableName, t_TreeStruc^.Id);
          Next;
        end;
      end;
    end;
  finally
    if p_TreeView.Items.Count > 0 then
      p_TreeView.Items[0].Expand(True);
    if t_ADOQuery.Active then
      t_ADOQuery.Close;
    FreeAndNil(t_ADOQuery);
  end;
end;

procedure LoadComboBoxItem(Sender: TcxComboBox; ParamName: string);
var
  qryParam: TUniQuery;
  i, j: integer;
  sParamValue, sRemark, sValue, sTmp: string;
  oValue, oList: TStringList;
begin
  Sender.Properties.Items.Clear;
  qryParam := nil;
  oList := nil;
  try
    GetADOQuery(qryParam, 'select * from ts_parameters where p_paramnm=' + QuotedStr(ParamName));
    sParamValue := Trim(qryParam.FieldByName('p_paramvalue').AsString);
    sValue := '';
    sRemark := Trim(qryParam.FieldByName('P_Remark').AsString);
    i := Pos('{', sRemark);
    // 单据是否审核后才能打印 {0:保存打印; 1:审核打印}
    if (i > 0) then
    begin
      sRemark := Trim(Copy(sRemark, i + 1, Length(sRemark) - i));
      i := Pos(':', sRemark);
      while (i > 0) do
      begin
        sValue := Trim(Copy(sRemark, 1, i - 1));

        j := Pos(';', sRemark);
        if (j > 0) then
        begin
          sTmp := Copy(sRemark, i + 1, j - i - 1);
          sRemark := Copy(sRemark, j + 1, Length(sRemark) - j);
          i := Pos(':', sRemark);
        end
        else
        begin
          sTmp := Copy(sRemark, i + 1, Length(sRemark) - i - 1);
          oValue := TStringList.Create;
          oValue.Add(sValue);
          if sValue = sParamValue then
          begin
            oList := oValue;
            Sender.Properties.Items.AddObject(sTmp, oValue);
          end
          else
            Sender.Properties.Items.AddObject(sTmp, oValue);
          break;
        end;
        oValue := TStringList.Create;
        oValue.Add(sValue);
        if sValue = sParamValue then
        begin
          oList := oValue;
          Sender.Properties.Items.AddObject(sTmp, oValue);
        end
        else
          Sender.Properties.Items.AddObject(sTmp, oValue);
      end;
    end;
    if (sParamValue <> '') and (oList <> nil) then
      Sender.ItemIndex := Sender.Properties.Items.IndexOfObject(oList)
    else if (Sender.Properties.Items.Count > 0) then
      Sender.ItemIndex := 0;
    qryParam.Close;
  finally
    // 此对象不能释放，否则下拉框选择后无法取值
    // if oValue <> nil then
    // FreeAndNil(oValue);
    if qryParam <> nil then
      FreeAndNil(qryParam);
  end;
end;

procedure LookupTableRefresh(ALookupID, ALookupParam: string); // 关联表重新打开
begin
  // 刷新数据集是要注意数据库一定要没有启动事物，不然会死锁
  GetLokupTbNotOpen(ALookupID, ALookupParam).Close;
  GetLokupTbNotOpen(ALookupID, ALookupParam).Open;
end;

procedure SetLookupField(AField: TField; AKeyFieldName: string; ALookupID, ALookupParam: string);
// 设置关联字段属性，从全局关联数组里找
var
  iLookupIndex: integer;
begin
  iLookupIndex := GetLookupTableIndex(ALookupID, ALookupParam);
  if iLookupIndex < 0 then
    exit;
  AField.FieldKind := fkLookup;
  AField.KeyFields := AKeyFieldName;
  AField.LookupCache := True;
  AField.LookupDataSet := TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet;
  AField.LookupKeyFields := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex]).DataSet.Tag)
    ^.KeyField;
  AField.LookupResultField := PLookupTableInfo(TDataSource(MainDM.LookupDS[iLookupIndex])
    .DataSet.Tag)^.ShowField;
end;

procedure TcxDBLookupOnKeyUp.ButtonClick(Sender: TObject; AButtonIndex: integer);
var
  P: TPoint;
begin
  P.X := 0;
  P.Y := 0;
  P := TWinControl(Sender).ClientToScreen(P); // GetCursorPos(P);
  P.X := P.X + +TWinControl(Sender).Width - 20;
  case AButtonIndex of
    1:
      popMenu.Popup(P.X, P.Y);
  end;
end;

constructor TcxDBLookupOnKeyUp.Create(AOwner: TComponent; LookupID: string;
  AShowStyle: TLookupComboxShowStyle;
  ShowField: string);
var
  iIndex, iListFieldIndex, iPos, iWidth: integer;
  sShowField, sListField, sListCaption, sTmpListCaption: string;
begin
  inherited Create(AOwner);
  FParentForm := nil;
  FFixDropFilter := False;
  FShowField := LowerCase(ShowField);
  edtComboBox := TcxDBLookupComboBox(AOwner);
  FBPopMenu := False;
  FFindMemo := TcxMemo.Create(AOwner);
  // FFindMemo.Name := 'FindMemo'+IntToStr(Integer(edtComboBox));
  if ParentForm <> nil then
    FFindMemo.Parent := ParentForm
  else if edtComboBox.Parent <> nil then
    FFindMemo.Parent := edtComboBox.Parent;
  FFindMemo.Visible := False;
  FFindMemo.Height := 30;
  FFindMemo.Properties.ReadOnly := True;
  FFindMemo.Enabled := False;
  FLookupID := LowerCase(LookupID);
  ListLookup := GetLokupTbNotOpen(FLookupID, '');
  ListDataSource := TDataSource.Create(Application); // (AOwner);
  // ListDataSet := TClientDataSet.Create(Application); //(AOwner);
  ListLookup.Filtered := True;
  ListLookup.Filter := '';
  ListDataSet := TUniQuery(ListLookup); // TSimpleDataSet
  if FLookupID = 'ts_kind' then
    ListDataSet.IndexFieldNames := 'kname';
  { -----------------------------------------------------------------------------
    Remark by Pegasus 2011-02-18
    Create(AOwner)
    AOwner:Application  将对象创建到应用程序容器里
    此时要处理对象名称重复问题
    ListDsProvider.Name := FLookupID + edtComboBox.DataBinding.DataField + IntToStr(Integer(edtComboBox));
    AOwner:nil          无容器创建
    ListDataSet.ProviderName 无法赋值，此赋值需要 ListDataSet，ListDsProvider对象在同一容器内
    AOwner:AOwner.Owner 将对象创建到窗体容器里
    窗体释放时会自动释放容器里的对象，造成TcxDBLookupOnKeyUp.Destroy方法无法工作（此方法里释放了ListDataSet，ListDsProvider）
    ----------------------------------------------------------------------------- }
  // ListDsProvider := TDataSetProvider.Create(Application); //(AOwner);
  // ListDsProvider.Name := FLookupID + edtComboBox.DataBinding.DataField + IntToStr(Integer(edtComboBox));
  // ListDsProvider.DataSet := ListLookup;
  // ListDataSet.ProviderName := FLookupID + edtComboBox.DataBinding.DataField + IntToStr(Integer(edtComboBox));
  // ListDataSet.SetProvider(ListDsProvider);
  // ListDataSet.Data := ListDsProvider.Data;
  // ListDataSet.Open;
  // FreeAndNil(ListDsProvider);
  ListDataSource.DataSet := ListDataSet;
  FOldFilterRecord := ListDataSet.OnFilterRecord;
  with edtComboBox do
  begin
    Properties.KeyFieldNames := PLookupTableInfo(ListLookup.Tag)^.KeyField;
    Properties.ListSource := ListDataSource;
    // 关联控件的显示方式
    case AShowStyle of
      lsKeyField:
        Properties.ListFieldNames := PLookupTableInfo(ListLookup.Tag)^.KeyField;
      lsShowField:
        Properties.ListFieldNames := PLookupTableInfo(ListLookup.Tag)^.ShowField;
      lsKey_Show_Fields:
        Properties.ListFieldNames := PLookupTableInfo(ListLookup.Tag)^.KeyField + ';' +
          PLookupTableInfo(ListLookup.Tag)
          ^.ShowField;
      lsShow_Key_Fields:
        Properties.ListFieldNames := PLookupTableInfo(ListLookup.Tag)^.ShowField + ';' +
          PLookupTableInfo(ListLookup.Tag)^.KeyField;
      lsListFields:
        Properties.ListFieldNames := PLookupTableInfo(ListLookup.Tag)^.ListField;
    end;
    sShowField := LowerCase(PLookupTableInfo(ListLookup.Tag)^.ShowField);
    sTmpListCaption := PLookupTableInfo(ListLookup.Tag)^.ListFieldCaption;
    Properties.ListOptions.ShowHeader := (sTmpListCaption <> '');
    iListFieldIndex := 0;
    iIndex := 0;
    while Pos('=', sTmpListCaption) > 0 do
    begin
      iPos := Pos('=', sTmpListCaption);
      sListField := LowerCase(Copy(sTmpListCaption, 0, iPos - 1));
      if (iListFieldIndex = 0) and (sListField = sShowField) then
      begin
        iListFieldIndex := iIndex;
      end;
      iIndex := iIndex + 1;
      sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      iPos := Pos('|', sTmpListCaption);
      iWidth := StrToIntDef(Copy(sTmpListCaption, 0, iPos - 1), 80);
      sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      iPos := Pos(';', sTmpListCaption);
      if iPos = 0 then
        sListCaption := Copy(sTmpListCaption, 0, Length(sTmpListCaption))
      else
      begin
        sListCaption := Copy(sTmpListCaption, 0, iPos - 1);
        sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      end;

      Properties.ListColumns.ColumnByFieldName(sListField).Caption := sListCaption;
      Properties.ListColumns.ColumnByFieldName(sListField).Width := iWidth;
      Properties.DropDownWidth := Properties.DropDownWidth + iWidth;
    end;
    Properties.ListFieldIndex := iListFieldIndex;
    Properties.DropDownSizeable := True;
    if (Properties.DropDownWidth = 0) then
      Properties.DropDownWidth := 220;
    if Properties.DropDownWidth < edtComboBox.Width then
      Properties.DropDownWidth := edtComboBox.Width;
  end;
  // ALookupID为空为控件已设置好数据连接，这里不再处理新增，修改，删除 Pegasus 2010-07-20
  if (FLookupID <> '') and (FLookupID <> 'ts_notetype') and (FLookupID <> 'ts_transporttype') and
    (FLookupID <> 'td_unitprice') and (FLookupID <> 'ts_traderlev') then
  begin
    popMenu := TPopupMenu.Create(nil); // (AOwner.Owner);
    muAdd := TMenuItem.Create(popMenu);
    muAdd.Caption := '新增 Ins';
    muAdd.OnClick := pmNewClick;
    popMenu.Items.Add(muAdd);
    muEdit := TMenuItem.Create(popMenu);
    muEdit.Caption := '修改 Alt+E';
    muEdit.OnClick := pmEditClick;
    popMenu.Items.Add(muEdit);
    muFind := TMenuItem.Create(popMenu);
    muFind.Caption := '查找 Alt+F';
    muFind.OnClick := pmFindClick;
    popMenu.Items.Add(muFind);
    // if (FLookupID <> 'td_product') and (FLookupID <> 'td_depot') then
    begin
      muEmpty := TMenuItem.Create(popMenu);
      muEmpty.Caption := '置空 Alt+D';
      muEmpty.OnClick := pmEmptyClick;
      popMenu.Items.Add(muEmpty);
    end;
    if edtComboBox.Properties.Buttons.Count > 1 then
      edtComboBox.Properties.Buttons.Items[1].Free;
    with edtComboBox.Properties.Buttons.Add do
      Kind := bkEllipsis;
    edtComboBox.Properties.OnButtonClick := ButtonClick;
  end
  else
  begin
    edtComboBox.Properties.ListOptions.ShowHeader := False;
    edtComboBox.Properties.DropDownWidth := edtComboBox.Width;
  end;
  FOldEnter := edtComboBox.OnEnter;
  FOldExit := edtComboBox.OnExit;
  edtComboBox.OnEnter := DBLookupComboBoxEnter;
  edtComboBox.OnExit := DBLookupComboBoxExit;
  // 设置下拉 以适应动态拼音查找 Pegasus 2010-07-15
  edtComboBox.Properties.DropDownListStyle := lsEditList; // lsEditFixedList;
  edtComboBox.Properties.IncrementalFiltering := False; // 此开关不能为True，否则输入中文会有乱码
  edtComboBox.Properties.CaseSensitiveSearch := False; //
  edtComboBox.Properties.DropDownSizeable := True; //
  edtComboBox.Properties.DropDownAutoSize := False; //
  edtComboBox.Properties.DropDownRows := g_iDropDownRows;
  edtComboBox.Properties.ImmediatePost := True; // 即时提交，不用失去焦点
  // 列表模式，  打开提高速度，  但无排序分组等功能  TcxLookupComboBox此处不能为True    否则输入中文会有乱码
  edtComboBox.Properties.GridMode := True; // 此开关不能为True，否则输入中文会有乱码
  edtComboBox.Properties.ListOptions.SyncMode := True; // 此开关不能为False，否则动态拼音查找不会联动
  edtComboBox.Properties.IncrementalSearch := False; // 关闭自动查询功能，解决中文匹配时的乱码问题  D7不支持UniCode
  edtComboBox.Properties.Revertable := False;
  edtComboBox.Properties.ValidateOnEnter := True; // 回车提交
  edtComboBox.Properties.ClearKey := ShortCut(Ord('D'), [ssAlt]); // scCtrl;
  // edtComboBox.Properties.HideSelection := True;

  if (edtComboBox.DataBinding.DataSource = nil) or (edtComboBox.DataBinding.DataSource.DataSet = nil)
  then
  begin
    raise Exception.Create('设置下拉控件错误！错误信息：没有指定数据源。');
  end;
  DataSet := edtComboBox.DataBinding.DataSource.DataSet;
  FedtField := nil;
  edtComboBox.OnKeyUp := DBLookupComboxKeyUp;
  edtComboBox.OnKeyDown := DBLookupComboxKeyDown;
  FOldChange := edtComboBox.Properties.OnChange;
  FOldPopup := edtComboBox.Properties.OnPopup;
  FOldCloseup := edtComboBox.Properties.OnCloseUp;
  // edtComboBox.Properties.OnChange := DBLookupComboBoxChange;
  edtComboBox.Properties.OnCloseUp := DBLookupComboBoxCloseup;
  edtComboBox.Properties.OnPopup := DBLookupComboBoxPopup;
  edtComboBox.Properties.DataController.OnSortingChanged := SortingChanged;
  FShowName := nil;
  if (FShowField = '') or (FShowField = edtComboBox.DataBinding.DataField) or
    (edtComboBox.DataBinding.DataSource.DataSet.FindField(FShowField) = nil) then
    ListDataSet.Open
  else
  begin
    if (not ListDataSet.Active) and
      (edtComboBox.DataBinding.DataSource.DataSet.FindField(FShowField) <> nil) then
    begin
      FShowName := TcxDBLabel.Create(edtComboBox.Owner);
      FShowName.Parent := edtComboBox.Parent;
      FShowName.Width := edtComboBox.Width - 2;
      FShowName.Top := edtComboBox.Top + 1;
      FShowName.Left := edtComboBox.Left + 1;
      FShowName.DataBinding.DataSource := edtComboBox.DataBinding.DataSource;
      FShowName.DataBinding.DataField := FShowField;
      FShowName.BringToFront;
      FShowName.OnClick := ShowNameClick;
      FShowName.Transparent := True;
    end;
  end;
end;

// procedure TcxDBLookupOnKeyUp.DBLookupComboBoxChange(Sender: TObject);
// begin
// if Assigned(FOldChange) then
// FOldChange(Sender);
// if not ListDataSet.Active then
// ListDataSet.Open;
// FFindHint := '';
// if Trim(edtComboBox.Text) = '' then
// begin
// edtComboBox.Properties.ValidateOnEnter := False;
// edtComboBox.Properties.ImmediatePost := False; //即时提交，不用失去焦点
// ListDataSet.OnFilterRecord := FOldFilterRecord;
// if FFilter = '' then
// ListDataSet.Filter := FOldFilter
// else
// ListDataSet.Filter := FFilter;
// ListDataSet.Filtered := FOldFiltered;
// if FixDropFilter then
// ListDataSet.Filtered := True;
// ShowFindHint;
// Exit;
// end;
// if not ListDataSet.Active or ListDataSet.IsEmpty then
// Exit;
// ListDataSet.DisableControls;
// ListDataSet.First;
// while not ListDataSet.Eof do
// begin
// if FindPy(edtComBoBox.Text) then
// begin
// FFindHint := StringReplace(edtComBoBox.Text, edtComboBox.SelText, '', []) + #13#10 + 'F5过滤'; //QuotedStr(edtComBoBox.Text) + #13#10 + 'F5过滤';
// Break;
// end;
// ListDataSet.Next;
// end;
// ShowFindHint;
// ListDataSet.EnableControls;
// 下拉列表获取光标
// SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_LEFT, 0);
// if ListDataSet.RecordCount = ListDataSet.RecNo then //if ListDataSet.Eof then
// begin
// SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
// SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
// end
// else
// begin
// SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
// SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
// end;
// end;

procedure TcxDBLookupOnKeyUp.DBLookupComboBoxCloseup(Sender: TObject);
begin
  if Assigned(FOldCloseup) then
    FOldCloseup(Sender);

  try
    ListDataSet.DisableControls;
    ListDataSet.OnFilterRecord := FOldFilterRecord;
    // if FFilter = '' then
    // ListDataSet.Filter := FOldFilter
    // else
    // ListDataSet.Filter := FFilter;
    // if FixDropFilter then
    // ListDataSet.Filtered := True
    // else
    // ListDataSet.Filtered := FOldFiltered;
    ListDataSet.Filtered := False;
  finally
    ListDataSet.EnableControls;
  end;
  FFindMemo.Visible := False;
  FFindMemo.Tag := 0;
  FBPopMenu := False;
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboBoxEnter(Sender: TObject);
begin
  if not ListDataSet.Active then
    ListDataSet.Open;
  FOldKeyPreview := ParentForm.KeyPreview;
  ParentForm.KeyPreview := False;
  if Assigned(FOldEnter) then
    FOldEnter(Sender);
  ListDataSet.OnFilterRecord := nil;
  if FFilter <> ListDataSet.Filter then
  begin
    ListDataSet.Filter := FFilter;
    ListDataSet.Filtered := True;
  end;
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboBoxExit(Sender: TObject);
begin
  ParentForm.KeyPreview := FOldKeyPreview;
  if Trim(edtComboBox.Text) = '' then
    TWinControl(Sender).Perform(WM_KEYDOWN, VK_ESCAPE, 0);
  if Assigned(FOldExit) then
    FOldExit(Sender);
  FFindMemo.Visible := False;
  FFindMemo.Tag := 0;
  try
    ListDataSet.DisableControls;
    ListDataSet.OnFilterRecord := FOldFilterRecord;
    ListDataSet.Filter := '';
    ListDataSet.Filtered := False;
  finally
    ListDataSet.EnableControls;
  end;
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboBoxPopup(Sender: TObject);
var
  P: TPoint;
  pForm: TPoint;
begin
  FBPopMenu := True;
  ShowName(False);
  if (Pos('td_product', FLookupID) > 0) and (GetSysParamValue('productinput') = '1') then
  begin
    muFind.Click;
    exit;
  end;
  if Assigned(FOldPopup) then
    FOldPopup(Sender);
  try
    ListDataSet.DisableControls;
    if FFilter <> ListDataSet.Filter then
      ListDataSet.Filter := FFilter;
    if ListDataSet.Filter <> '' then
      ListDataSet.Filtered := True;
    if (Trim(ListDataSet.KeyFields) <> '') and (edtField <> nil) and not edtField.IsNull then
    begin
      ListDataSet.FindKey([edtField.Value]);
    end;
  finally
    ListDataSet.EnableControls;
  end;
  FFindMemo.Tag := 1;
  if (g_iLKTop > 0) and (FParentForm <> nil) then
  begin
    P.X := 0;
    P.Y := 0;
    pForm.X := 0;
    pForm.Y := 0;
    Windows.ClientToScreen(edtComboBox.Handle, P);
    Windows.ClientToScreen(FParentForm.Height, pForm);
    edtComboBox.Properties.Grid.Parent.Top := P.Y + pForm.Y + g_iLKTop;
  end;
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboxFilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
  sPy, sListField, sTmpListField: string;
  sFilter, sOld: widestring;
  iPos: integer;
begin
  if Assigned(FOldFilterRecord) then
  begin
    FOldFilterRecord(DataSet, Accept);
    if not Accept then
      exit;
  end;
  if edtComboBox = nil then
    exit;
  if edtComboBox.Properties = nil then
    exit;
  sFilter := LowerCase(FFindText);
  if (sFilter <> '') and (edtComboBox.Properties.ListColumns.Count > 0) then
  begin
    Accept := False;
    sTmpListField := edtComboBox.Properties.ListFieldNames;
    while Pos(';', sTmpListField) > 0 do
    begin
      iPos := Pos(';', sTmpListField);
      sListField := Copy(sTmpListField, 0, iPos - 1);
      sPy := LowerCase(ChnToPY(DataSet.FieldByName(sListField).AsString));
      sOld := LowerCase(DataSet.FieldByName(sListField).AsString);
      Accept := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
      sTmpListField := Copy(sTmpListField, iPos + 1, Length(sTmpListField) - iPos);
      if Accept then
        exit;
    end;
    if sTmpListField <> '' then
    begin
      sPy := LowerCase(ChnToPY(DataSet.FieldByName(sTmpListField).AsString));
      sOld := LowerCase(DataSet.FieldByName(sTmpListField).AsString);
      Accept := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
    end;
    if Accept then
      exit;
  end
  else
    Accept := True;
  if Assigned(FOldFilterRecord) then
    FOldFilterRecord(DataSet, Accept);
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboxKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_RETURN:
      begin
        if not FBPopMenu then
        begin
          Key := 0;
          TWinControl(Sender).Perform(WM_KEYDOWN, VK_TAB, 0);
          exit;
        end;
        if (ListDataSet.Eof and not FindPy(edtComboBox.Text)) or (Trim(edtComboBox.Text) = '') then
        begin
          ListDataSet.FindKey([edtField.Value]);
          edtComboBox.DataBinding.DataSource.DataSet.Cancel;
          TWinControl(Sender).Perform(WM_KEYDOWN, VK_ESCAPE, 0);
          // if edtField<>nil then
          // SetFieldValNoChg(edtField.DataSet, edtField.FieldName, edtField.OldValue);
          // edtComboBox.Properties.Changed;
        end
        else
        begin
          // if edtField <> nil then
          // edtField.Value := ListDataSet.FieldByName(edtComboBox.Properties.KeyFieldNames).Value;
          // TWinControl(Sender).Perform(WM_KEYDOWN, VK_TAB, 0);
        end;
      end;
  end;
end;

procedure TcxDBLookupOnKeyUp.DBLookupComboxKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  sText, sSelText: widestring;
begin
  case Key of
    VK_UP:
      ;
    VK_DOWN:
      ;
    VK_F2:
      begin
        ListDataSet.OnFilterRecord := DBLookupComboxFilterRecord;
        ListDataSet.Filtered := True;
      end;
    VK_INSERT:
      pmNewClick(nil);
    VK_F5:
      begin
        ListDataSet.DisableControls;
        FOldFilter := ListDataSet.Filter;
        FOldFiltered := ListDataSet.Filtered;
        // ListDataSet.Filtered := False;
        ListDataSet.OnFilterRecord := DBLookupComboxFilterRecord;
        ListDataSet.Filtered := True;
        ListDataSet.EnableControls;
      end;
    VK_F6:
      begin
        FOldFilter := ListDataSet.Filter;
        FOldFiltered := ListDataSet.Filtered;
        ListDataSet.OnFilterRecord := DBLookupComboxFilterRecord;
        ListDataSet.Filtered := True;
      end;
    Ord('F'):
      begin
        if (ssAlt in Shift) then // if (ssCtrl in Shift) then
          pmFindClick(nil);
      end;
    Ord('E'):
      begin
        if (ssAlt in Shift) then // if (ssCtrl in Shift) then
          pmEditClick(nil);
      end;
    Ord('I'):
      begin
        if (ssAlt in Shift) then // if (ssCtrl in Shift) then
          pmNewClick(nil);
      end;
  else
    begin
      sText := edtComboBox.Text;
      sSelText := Copy(sText, edtComboBox.SelStart + 1, edtComboBox.SelLength); // 处理中文
      // if (Length(sText) > 1) and not (sText[Length(sText)] < #160) then //判断最后一个字是否中文
      // begin
      // if sText[Length(sText)] = sText[Length(sText) - 1] then //判断最后两个字中文是否相同，是的话去掉一个
      // begin
      // sText := Copy(sText, 1, Length(sText) - 1);
      // if edtComboBox.Text <> sText + sSelText then
      // begin
      // edtComboBox.Text := sText + sSelText;
      // edtComboBox.SelStart := Length(sText);
      // edtComboBox.SelLength := Length(sSelText);
      // end;
      // end;
      // end;
      FFindHint := QuotedStr(Trim(AnsiReplaceStr(sText, sSelText, ''))) + #13#10 + 'F5过滤';
      FFindText := Trim(AnsiReplaceStr(sText, sSelText, ''));
      if ((Pos('td_product', FLookupID) > 0) and (GetSysParamValue('productinput') = '2')) or
        ((Pos('tfd_subject', FLookupID) > 0) and (GetSysParamValue('subjectinput') = '1')) or
        ((Pos('td_trader', FLookupID) > 0) and (GetSysParamValue('traderinput') = '1')) then
      begin
        ListDataSet.DisableControls;
        FOldFilter := ListDataSet.Filter;
        FOldFiltered := ListDataSet.Filtered;
        // ListDataSet.Filtered := False;
        ListDataSet.OnFilterRecord := DBLookupComboxFilterRecord;
        ListDataSet.Filtered := True;
        ListDataSet.EnableControls;
      end
      else
      begin

        if not ListDataSet.Active then
          ListDataSet.Open;

        if Trim(edtComboBox.Text) = '' then
        begin
          // edtComboBox.Properties.ValidateOnEnter := False;
          // edtComboBox.Properties.ImmediatePost := False; //即时提交，不用失去焦点
          ListDataSet.OnFilterRecord := FOldFilterRecord;
          if FFilter = '' then
            ListDataSet.Filter := FOldFilter
          else
            ListDataSet.Filter := FFilter;
          if FixDropFilter then
            ListDataSet.Filtered := True
          else if ListDataSet.Filtered <> FOldFiltered then
            ListDataSet.Filtered := FOldFiltered;
          ShowFindHint;
          exit;
        end;
        if not ListDataSet.Active or ListDataSet.IsEmpty then
          exit;
        ListDataSet.DisableControls;
        // ListDataSet.Filter := '';
        // ListDataSet.Filtered := False;
        ListDataSet.First;
        while not ListDataSet.Eof do
        begin
          if FindPy(FFindText) then
          begin
            FFindHint := Trim(StringReplace(edtComboBox.Text, edtComboBox.SelText, '', [])) +
              #13#10 + 'F5过滤';
            // QuotedStr(edtComBoBox.Text) + #13#10 + 'F5过滤';
            break;
          end;
          ListDataSet.Next;
        end;
        ShowFindHint;
        // 下拉列表获取光标
        ListDataSet.EnableControls;
        if ListDataSet.RecNo = 1 then
        begin
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
        end
        else if ListDataSet.Eof then
        begin
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
        end
        else
        begin
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
          SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
        end;
      end;
    end;
  end;
end;

destructor TcxDBLookupOnKeyUp.Destroy;
var
  i: integer;
begin
  // Properties会被自动释放，需要判断
  FFixDropFilter := False;
  if (edtComboBox <> nil) and (edtComboBox.Properties <> nil) then
  begin
    for i := edtComboBox.Properties.Buttons.Count - 1 downto 1 do
      edtComboBox.Properties.Buttons.Items[i].Free;
    edtComboBox.Properties.OnButtonClick := nil;
    if Assigned(FOldChange) then
      edtComboBox.Properties.OnChange := FOldChange
    else
      edtComboBox.Properties.OnChange := nil;
    if Assigned(FOldCloseup) then
      edtComboBox.Properties.OnCloseUp := FOldCloseup
    else
      edtComboBox.Properties.OnCloseUp := nil;
    if Assigned(FOldPopup) then
      edtComboBox.Properties.OnPopup := FOldPopup
    else
      edtComboBox.Properties.OnPopup := nil;
    if Assigned(FOldEnter) then
      edtComboBox.OnEnter := FOldEnter
    else
      edtComboBox.OnEnter := nil;
    if Assigned(FOldExit) then
      edtComboBox.OnExit := FOldExit
    else
      edtComboBox.OnExit := nil;
  end;
  FFilter := '';
  FreeAndNil(muAdd);
  FreeAndNil(muEdit);
  FreeAndNil(muFind);
  FreeAndNil(muEmpty);
  FreeAndNil(popMenu);
  if edtComboBox <> nil then
  begin
    if ListDataSource <> nil then
      FreeAndNil(ListDataSource);
    if ListDsProvider <> nil then
      FreeAndNil(ListDsProvider);
    if ListDataSet <> nil then
      ListDataSet := nil;
    // 此指针为引用GetLookupTabels不能释放
    // FreeAndNil(ListDataSet);
    if ListLookup <> nil then
      ListLookup := nil;
  end;
  inherited;
end;

function TcxDBLookupOnKeyUp.FindPy(const sSubStr: widestring): Boolean;
var
  sPy, sListField, sTmpListField: string;
  sFilter, sOld: widestring;
  iPos: integer;
begin
  sFilter := LowerCase(sSubStr);
  if (sFilter <> '') and (edtComboBox.Properties.ListColumns.Count > 0) then
  begin
    if (Pos('tfd_subject', FLookupID) > 0) and (IsDigit(sFilter)) then
    begin
      Result := Copy(ListDataSet.FieldByName('subcode').AsString, 1, Length(sFilter)) = sFilter;
      exit;
    end;

    Result := False;
    sTmpListField := Trim(edtComboBox.Properties.ListFieldNames);
    while (Pos(';', sTmpListField) > 0) or (sTmpListField <> '') do
    begin
      iPos := Pos(';', sTmpListField);
      if iPos > 0 then
        sListField := Copy(sTmpListField, 0, iPos - 1)
      else
      begin
        sListField := sTmpListField;
        sTmpListField := '';
      end;
      sPy := LowerCase(ChnToPY(ListDataSet.FieldByName(sListField).AsString));
      sOld := LowerCase(ListDataSet.FieldByName(sListField).AsString);
      Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
      sTmpListField := Copy(sTmpListField, iPos + 1, Length(sTmpListField) - iPos);
      if Result then
        exit;
    end;
    if sTmpListField <> '' then
    begin
      sPy := LowerCase(ChnToPY(ListDataSet.FieldByName(sTmpListField).AsString));
      sOld := LowerCase(ListDataSet.FieldByName(sTmpListField).AsString);
      Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
    end;
    if Result then
      exit;
  end
  else
    Result := True;
end;

function TcxDBLookupOnKeyUp.GetedtField: TField;
begin
  FedtField := nil;
  if DataSet.Active and (DataSet.FindField(edtComboBox.DataBinding.DataField) <> nil) then
    FedtField := DataSet.FieldByName(edtComboBox.DataBinding.DataField);
  Result := FedtField;
end;

procedure TcxDBLookupOnKeyUp.ShowName(Show: Boolean);
begin
  if FShowName <> nil then
    FShowName.Visible := Show;
end;

procedure TcxDBLookupOnKeyUp.SortingChanged(Sender: TObject);
var
  sField, sIndex: string;
begin
  sField := '';
  sIndex := '';
  if edtComboBox.Properties.DataController.GetSortingItemCount > 0 then
  begin
    sField := edtComboBox.Properties.DataController.GetItemFieldName
      (edtComboBox.Properties.DataController.GetSortingItemIndex(0));
    if ListDataSet.IndexFieldNames = sField then
      sIndex := sField + ' desc'
    else
      sIndex := sField;
  end;
  ListDataSet.IndexFieldNames := sIndex;
end;

procedure TcxDBLookupOnKeyUp.SynShowNamePos;
begin
  if (FShowName <> nil) and (edtComboBox <> nil) then
  begin
    if FShowName.Visible then
    begin
      FShowName.Parent := edtComboBox.Parent;
      FShowName.Width := edtComboBox.Width - 2;
      FShowName.Top := edtComboBox.Top + 1;
      FShowName.Left := edtComboBox.Left + 1;
      FShowName.Anchors := FShowName.Anchors - [akTop];
      FShowName.Anchors := FShowName.Anchors + [akBottom];
    end;
  end;
end;

function TcxDBLookupOnKeyUp.ParentForm: TfrmBaseForm;
var
  FParent: TWinControl;
begin
  Result := nil;
  if FParentForm <> nil then
  begin
    Result := FParentForm;
    exit;
  end;
  FParent := TWinControl(edtComboBox).Parent;
  if FParent = nil then
    exit;
  while not(FParent is TfrmBaseForm) do
  begin
    FParent := TWinControl(FParent).Parent;
  end;
  if FParent <> nil then
    FParentForm := TfrmBaseForm(FParent)
  else
  begin
    ShowErrorBox('获取下拉父件错误！');
  end;
  Result := FParentForm;
end;

procedure TcxDBLookupOnKeyUp.pmEditClick(Sender: TObject);
begin
  PopLookup(et_Modify);
end;

procedure TcxDBLookupOnKeyUp.pmEmptyClick(Sender: TObject);
begin
  if not(DataSet.State in [dsEdit, dsInsert]) then
    DataSet.Edit;
  DataSet.FieldByName(edtComboBox.DataBinding.DataField).Clear;
end;

procedure TcxDBLookupOnKeyUp.pmFindClick(Sender: TObject);
begin
  PopLookup(et_Browser);
end;

procedure TcxDBLookupOnKeyUp.pmNewClick(Sender: TObject);
begin
  PopLookup(et_New);
end;

procedure TcxDBLookupOnKeyUp.PopLookup(EditType: TEditType);
var
  oForm: TfrmBaseChildcxGridBtnQueryForm;
  dForm: TfrmBaseDataForm;
  tForm: TFrmBaseTreeForm;
  // AQuery: TUniQuery;
  sForm, sFormCaption, sID: string;
  DllFun: TDLLFunction;
  iForm, iProductID: integer;
  OldActivate: TNotifyEvent;
begin
  OldActivate := ParentForm.OnActivate;
  ParentForm.OnActivate := nil;
  // if GetADOQuery(AQuery, 'select * from ts_DynamicEditFormMst where et_layoutid=-1 and et_Table_name=' + QuotedStr(FLookupID)) > 0 then
  // begin
  // sForm := AQuery.FieldByName('et_editformid').AsString;
  // sFormCaption := AQuery.FieldByName('et_formcaption').AsString;
  // end
  // else
  sFormCaption := '';
  if (LowerCase(FLookupID) = 'td_trader') and (FFilter <> '') then
  begin
    if Pos('client', FFilter) > 0 then
      FLookupID := 'td_traderclient'
    else if Pos('provider', FFilter) > 0 then
      FLookupID := 'td_traderprovider';
  end;
  DllFun := MainDM.GetModuleItemForEdit(FLookupID, sFormCaption);
  if DllFun <> nil then
  begin
    if sFormCaption = '' then
    begin
      sForm := IntToStr(DllFun.ModuleFuncID);
      sFormCaption := DllFun.ModuleFuncName;
    end;
    if not((FLookupID = 'td_product') and (EditType = et_Browser)) then // 单独处理货品查找
      MainDM.GetUserInfo.ActiveDllFunction := DllFun;
    case EditType of
      et_New:
        begin
          if not DllFun.DefaultRights.bAdd then
          begin
            ShowWarningBox('您没有新增' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
      et_Modify:
        begin
          if not DllFun.DefaultRights.bEdit then
          begin
            ShowWarningBox('您没有修改' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
      et_Browser:
        begin
          if not DllFun.DefaultRights.bBrowse then
          begin
            ShowWarningBox('您没有浏览' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
    end;
  end;
  try
    // 通用基础资料窗口
    if (FLookupID = 'td_post') or (FLookupID = 'td_money') or (FLookupID = 'td_filiale') or
      (FLookupID = 'td_workshop')
      or (FLookupID = 'td_workertype') or (FLookupID = 'td_workproc') or (FLookupID = 'td_workline') or
      (FLookupID = 'td_emptype') or (FLookupID = 'td_bank') or (FLookupID = 'td_servicetype') or
      (FLookupID = 'td_repairtype') or (FLookupID = 'td_machine') or (FLookupID = 'td_commistype') or
      (FLookupID = 'td_comexpenditure') or (FLookupID = 'td_producttrademark') or
      (FLookupID = 'td_oa_flow') or
      (FLookupID = 'td_oa_type') or (FLookupID = 'td_oa_items') or (FLookupID = 'td_oa_template') or
      (FLookupID = 'tfd_project') or (FLookupID = 'tfd_vouword') or
      (FLookupID = 'tfd_vousummarytype') then
    begin
      if DllFun = nil then
        exit;
      // if (DataSet.State in [dsEdit, dsInsert]) then
      // DataSet.Post;
      try
        oForm := TfrmBaseChildcxGridBtnQueryForm.Create(nil, sFormCaption, True);
        if CreatesDynamicEditForm(sForm, sFormCaption, DllFun, oForm) then
        begin
          case EditType of
            et_New:
              begin
                oForm.LocateRecord(TMP_ID);
                oForm.btnNew.OnClick(nil);
              end;
            et_Modify:
              begin
                if edtField.IsNull then
                  oForm.LocateRecord(-1)
                else
                begin
                  oForm.LocateRecord(edtField.AsInteger);
                  oForm.btnEdit.Click;
                end;
              end;
            et_Browser:
              begin
                oForm.LocateRecord(-1);
              end;
          end;
          if oForm.ShowModal = mrOk then
          begin
            RefreshData(False);
            SetDropDownFilter(FFilter);
            if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([oForm.GetKeyValue]) then
            begin
              if not(DataSet.State in [dsEdit, dsInsert]) then
                DataSet.Edit;
              edtField.Value := oForm.GetKeyValue;
            end;
            ListDataSet.Filtered := False;
          end;
          oForm := nil; // ShowModal = mrOk 会触发FormDestroy
        end;
      finally
        // AQuery.Close;
        // DllFun:=MainDM.GetModuleItemForEdit(FLookupID, sFormCaption) 不能释放
        // FreeAndNil(DllFun);
        // FreeAndNil(AQuery);
        ParentForm.BringToFront;
      end;
    end
    else if (FLookupID = 'td_product') or (FLookupID = 'td_trader') or
      (FLookupID = 'td_traderthird') or
      (FLookupID = 'td_traderclient') or (FLookupID = 'td_traderprovider') or
      (FLookupID = 'td_empmaster') or
      (FLookupID = 'td_depot') or (FLookupID = 'td_balance') or (FLookupID = 'td_account') or
      (FLookupID = 'td_depotkind') or (FLookupID = 'td_depottype') or (FLookupID = 'tfd_subject') or
      (FLookupID = 'tfd_subject_all') or (FLookupID = 'td_productbomver') or
      (FLookupID = 'td_productbomvernotrader') or
      (FLookupID = 'td_productbomformula') then
    begin
      dForm := nil;
      if (FLookupID = 'td_product') and (EditType = et_Browser) then // 单独处理货品查找
      begin
        if ParentForm is TfrmBaseParticularQueryForm then
          TfrmBaseParticularQueryForm(ParentForm).ChoiceProduct
        else if Pos('Bill', ParentForm.ClassName) > 0 then
        begin
          if LowerCase(edtComboBox.Name) = 'edtproduct' then
            GlobalCallProc('bill', 'td_productdtl', IntToStr(integer(ParentForm)))
          else
            GlobalCallProc('bill', FLookupID, IntToStr(integer(ParentForm)));
        end
        else
        begin
          ChoiceProductQuery(ParentForm, edtField);
        end;
      end
      else
      begin
        if EditType = et_New then
          sID := IntToStr(TMP_ID)
        else if EditType = et_Modify then
        begin
          if (edtField = nil) or edtField.IsNull then
            sID := IntToStr(-1)
          else
            sID := edtField.AsString
        end
        else if EditType = et_Browser then
          sID := '-1';
        if Pos('tfd_', FLookupID) > 0 then
          iForm := StrToInt(VarToStr(GetGlobalCallValue('finance', FLookupID, sID)))
        else if (FLookupID = 'td_productbomver') or (FLookupID = 'td_productbomvernotrader') then
        begin
          iProductID := -1;
          if (edtField = nil) or edtField.IsNull then
            sID := IntToStr(-1)
          else
          begin
            sID := edtField.AsString;
            if edtField.DataSet.FindField('productid') <> nil then
              iProductID := GetFieldVal(edtField.DataSet, 'productid', -1);
          end;
          iForm := StrToInt(VarToStr(GetGlobalCallValue('manufacture', FLookupID,
            VarArrayOf([iProductID, StrToIntDef(sID, -1)]))));
        end
        else if (FLookupID = 'td_productbomformula') then
          iForm := StrToInt(VarToStr(GetGlobalCallValue('manufacture', FLookupID, sID)))
        else
          iForm := StrToInt(VarToStr(GetGlobalCallValue('basedata', FLookupID, sID)));
        dForm := TfrmBaseDataForm(iForm);
      end;
      if (dForm = nil) then
        exit;
      if dForm.FormStyle <> fsNormal then // 确保窗体可以 ShowModal
        dForm.FormStyle := fsNormal;
      if (dForm.ShowModal = mrOk) then
      begin
        // if (EditType = et_Browser) and (dForm is TfrmBaseChildForm) then
        // TfrmBaseChildForm(dForm).mnuQueryRecords.Click;
        RefreshData(False);
        SetDropDownFilter(FFilter);
        if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([dForm.GetKeyValue]) then
        begin
          if not(DataSet.State in [dsEdit, dsInsert]) then
            DataSet.Edit;
          edtField.Value := dForm.GetKeyValue;
        end;
        ListDataSet.Filtered := False;
      end;
      // dForm := nil;
    end
    // 树状基础资料
    else if (FLookupID = 'td_dept') or (FLookupID = 'td_expend') or (FLookupID = 'td_area') or
      (FLookupID = 'td_producttype') or (FLookupID = 'td_tradertype') or
      (FLookupID = 'td_productbompart') then
    begin
      if EditType = et_New then
        sID := 'TMP_ID'
      else if EditType = et_Modify then
      begin
        if edtField.IsNull then
          sID := '-1'
        else
          sID := edtField.AsString;
      end;
      iForm := StrToInt(VarToStr(GetGlobalCallValue('basedata', FLookupID, sID)));
      try
        tForm := TFrmBaseTreeForm(iForm);
      except
        exit;
      end;
      if (tForm = nil) then
        exit;
      tForm.Selected := True;
      if (tForm.ShowModal = mrOk) then
      begin
        RefreshData(False);
        SetDropDownFilter(FFilter);
        if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([tForm.GetKeyValue]) then
        begin
          if not(DataSet.State in [dsEdit, dsInsert]) then
            DataSet.Edit;
          edtField.Value := tForm.GetKeyValue;
        end;
        ListDataSet.Filtered := False;
      end;
      tForm := nil; // ShowModal = mrOk 会触发FormDestroy
    end;
  finally
    ParentForm.OnActivate := OldActivate;
  end;
end;

{ TcxDBDateEditKeyDown }

constructor TcxDBDateEditKeyDown.Create(AOwner: TComponent);
begin
  inherited Create(AOwner.Owner);
  edtDateEdit := TcxDateEdit(AOwner);
  edtDateEdit.OnKeyDown := DBDateEditKeyDown;
end;

procedure TcxDBDateEditKeyDown.DBDateEditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  iTmp, iYear, iMonth, iDay, iHour, iMin, iSec, iMilSec: Word;
  Date: TDateTime;
begin
  DecodeDateTime(TcxDateEdit(Sender).Date, iYear, iMonth, iDay, iHour, iMin, iSec, iMilSec);
  if Key = VK_LEFT then
  begin
    Key := 0;
    if TcxDateEdit(Sender).SelStart > 7 then
      TcxDateEdit(Sender).SetSelection(5, 2)
    else if TcxDateEdit(Sender).SelStart > 4 then
      TcxDateEdit(Sender).SetSelection(0, 4);
  end
  else if Key = VK_RIGHT then
  begin
    Key := 0;
    if TcxDateEdit(Sender).SelStart < 4 then
      TcxDateEdit(Sender).SetSelection(5, 2)
    else if TcxDateEdit(Sender).SelStart < 7 then
      TcxDateEdit(Sender).SetSelection(8, 2);
  end
  else if Key = VK_UP then
  begin
    Key := 0;
    if TcxDateEdit(Sender).SelStart < 4 then
    begin
      iTmp := YearOf(TcxDateEdit(Sender).Date) + 1;
      TcxDateEdit(Sender).Date := EncodeDateTime(iTmp, MonthOf(TcxDateEdit(Sender).Date),
        DayOf(TcxDateEdit(Sender).Date), iHour, iMin, iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(0, 4);
    end
    else if TcxDateEdit(Sender).SelStart < 7 then
    begin
      iTmp := MonthOf(TcxDateEdit(Sender).Date) + 1;
      if iTmp > 12 then
        iTmp := 12;
      iDay := DayOf(TcxDateEdit(Sender).Date);
      if (iTmp = 2) and (iDay > 28) then
        iDay := 28
      else if (iTmp in [4, 6, 9, 11]) and (iDay > 30) then
        iDay := 30;
      TcxDateEdit(Sender).Date := EncodeDateTime(YearOf(TcxDateEdit(Sender).Date), iTmp, iDay,
        iHour, iMin,
        iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(5, 2);
    end
    else
    begin
      Date := TcxDateEdit(Sender).Date + 1;
      TcxDateEdit(Sender).Date := EncodeDateTime(YearOf(Date), MonthOf(Date), DayOf(Date), iHour,
        iMin, iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(8, 2);
    end;
  end
  else if Key = VK_DOWN then
  begin
    Key := 0;
    if TcxDateEdit(Sender).SelStart < 4 then
    begin
      iTmp := YearOf(TcxDateEdit(Sender).Date) - 1;
      TcxDateEdit(Sender).Date := EncodeDateTime(iTmp, MonthOf(TcxDateEdit(Sender).Date),
        DayOf(TcxDateEdit(Sender).Date), iHour, iMin, iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(0, 4);
    end
    else if TcxDateEdit(Sender).SelStart < 7 then
    begin
      iTmp := MonthOf(TcxDateEdit(Sender).Date) - 1;
      iDay := DayOf(TcxDateEdit(Sender).Date);
      if iTmp <= 0 then
        iTmp := 1;
      if (iTmp = 2) and (iDay > 28) then
        iDay := 28
      else if (iTmp in [4, 6, 9, 11]) and (iDay > 30) then
        iDay := 30;
      TcxDateEdit(Sender).Date := EncodeDateTime(YearOf(TcxDateEdit(Sender).Date), iTmp, iDay,
        iHour, iMin,
        iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(5, 2);
    end
    else
    begin
      Date := TcxDateEdit(Sender).Date - 1;
      TcxDateEdit(Sender).Date := EncodeDateTime(YearOf(Date), MonthOf(Date), DayOf(Date), iHour,
        iMin, iSec, iMilSec);
      TcxDateEdit(Sender).SetSelection(8, 2);
    end;
  end;
  if Key = 0 then
  begin
    if Sender is TcxDBDateEdit then
    begin
      if not(TcxDBDateEdit(Sender).DataBinding.DataSource.DataSet.State in [dsEdit, dsInsert]) then
        TcxDBDateEdit(Sender).DataBinding.DataSource.DataSet.Edit;
      TcxDBDateEdit(Sender).DataBinding.DataSource.DataSet.FieldByName
        (TcxDBDateEdit(Sender).DataBinding.DataField)
        .AsDateTime := TcxDBDateEdit(Sender).Date;
    end;
  end;
end;

{ TcxDBColumnOnChange }

procedure TcxDBColumnOnChange.ButtonClick(Sender: TObject; AButtonIndex: integer);
var
  P: TPoint;
begin
  P.X := 0;
  P.Y := 0;
  P := TWinControl(Sender).ClientToScreen(P); // GetCursorPos(P);
  P.X := P.X + +TWinControl(Sender).Width - 20;
  case AButtonIndex of
    1:
      popMenu.Popup(P.X, P.Y);
  end;
end;

constructor TcxDBColumnOnChange.Create(ParentForm: TfrmBaseForm; AOwner: TComponent;
  LookupID, ShowField: string);
var
  iIndex, iListFieldIndex, iPos, iWidth: integer;
  sShowField, sListField, sListCaption, sTmpListCaption: string;
begin
  FFixDropFilter := False;
  FLookupID := LowerCase(LookupID);
  FShowField := LowerCase(ShowField);
  FParentForm := ParentForm;
  FFindMemo := TcxMemo.Create(AOwner);
  if ParentForm <> nil then
    FFindMemo.Parent := ParentForm;
  FFindMemo.Visible := False;
  FFindMemo.Height := 30;
  FFindMemo.Properties.ReadOnly := True;
  FFindMemo.Enabled := False;
  FSetValue := False;

  edtColumn := TcxGridDBBandedColumn(AOwner);
  ListDataSource := TDataSource.Create(AOwner);
  // ListDataSet := TClientDataSet.Create(AOwner);
  ListLookup := GetLokupTbNotOpen(FLookupID, '');
  FOldFiltered := ListLookup.Filtered;
  FOldFilter := ListLookup.Filter;
  ListLookup.Filtered := False;
  ListLookup.Filter := '';
  // ListLookup.First;
  // ListDsProvider := TDataSetProvider.Create(AOwner);
  // if ParentForm <> nil then
  // begin
  // ListDsProvider.Name := FLookupID + ParentForm.Name;
  // ListDataSet.ProviderName := FLookupID + ParentForm.Name;
  // end
  // else
  // begin
  // ListDsProvider.Name := FLookupID;
  // ListDataSet.ProviderName := FLookupID;
  // end;
  // ListDsProvider.DataSet := ListLookup;
  // ListDataSet.SetProvider(ListDsProvider);
  // ListDataSet.Data := ListDsProvider.Data;
  ListDataSet := TUniQuery(ListLookup); // TSimpleDataSet(ListLookup);

  // FreeAndNil(ListDsProvider);
  ListDataSource.DataSet := ListDataSet;
  FOldFilterRecord := ListDataSet.OnFilterRecord;
  FListField := TStringList.Create;
  with edtColumn do
  begin
    // if PropertiesClass = TcxCustomEditProperties then
    PropertiesClass := TcxLookupComboBoxProperties;
    // if Properties = nil then
    // Properties := TcxLookupComboBoxProperties.Create(nil);
    // if Properties = nil then Exit;
    Properties.BeginUpdate;
    TcxLookupComboBoxProperties(Properties).UseMouseWheel := False; // 关闭鼠标滚轮移动下拉记录
    TcxLookupComboBoxProperties(Properties).Revertable := False; // 关闭鼠标双击选择下一个
    TcxLookupComboBoxProperties(Properties).ListSource := ListDataSource;
    TcxLookupComboBoxProperties(Properties).KeyFieldNames := PLookupTableInfo(ListLookup.Tag)
      ^.KeyField;
    TcxLookupComboBoxProperties(Properties).ListFieldNames := PLookupTableInfo(ListLookup.Tag)
      ^.ListField;
    sShowField := LowerCase(PLookupTableInfo(ListLookup.Tag)^.ShowField);
    sTmpListCaption := PLookupTableInfo(ListLookup.Tag)^.ListFieldCaption;
    TcxLookupComboBoxProperties(Properties).ListOptions.ShowHeader := (sTmpListCaption <> '');
    iListFieldIndex := 0;
    iIndex := 0;
    while Pos('=', sTmpListCaption) > 0 do
    begin
      iPos := Pos('=', sTmpListCaption);
      sListField := LowerCase(Copy(sTmpListCaption, 0, iPos - 1));
      if (iListFieldIndex = 0) and (sListField = sShowField) then
      begin
        iListFieldIndex := iIndex;
      end;
      FListField.Add(sListField);
      iIndex := iIndex + 1;
      sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      iPos := Pos('|', sTmpListCaption);
      iWidth := StrToIntDef(Copy(sTmpListCaption, 0, iPos - 1), 80);
      sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      iPos := Pos(';', sTmpListCaption);
      if iPos = 0 then
        sListCaption := Copy(sTmpListCaption, 0, Length(sTmpListCaption))
      else
      begin
        sListCaption := Copy(sTmpListCaption, 0, iPos - 1);
        sTmpListCaption := Copy(sTmpListCaption, iPos + 1, Length(sTmpListCaption) - iPos);
      end;
      TcxLookupComboBoxProperties(Properties).ListColumns.ColumnByFieldName(sListField).Caption :=
        sListCaption;
      TcxLookupComboBoxProperties(Properties).ListColumns.ColumnByFieldName(sListField).Width
        := iWidth;
      TcxLookupComboBoxProperties(Properties).DropDownWidth :=
        TcxLookupComboBoxProperties(Properties)
        .DropDownWidth + iWidth;
    end;
    TcxLookupComboBoxProperties(Properties).ListFieldIndex := iListFieldIndex;
    if (TcxLookupComboBoxProperties(Properties).DropDownWidth = 0) then
      TcxLookupComboBoxProperties(Properties).DropDownWidth := 220;
    if TcxLookupComboBoxProperties(Properties).DropDownWidth < edtColumn.Width then
      TcxLookupComboBoxProperties(Properties).DropDownWidth := edtColumn.Width;

    TcxLookupComboBoxProperties(Properties).AutoSelect := False;
    TcxLookupComboBoxProperties(Properties).UseLeftAlignmentOnEditing := False;
    TcxLookupComboBoxProperties(Properties).DropDownListStyle := lsEditList;
    TcxLookupComboBoxProperties(Properties).IncrementalFiltering := False; // 增量过滤关闭，打开时无法使用拼音首字母
    TcxLookupComboBoxProperties(Properties).CaseSensitiveSearch := False;
    TcxLookupComboBoxProperties(Properties).DropDownSizeable := True;
    TcxLookupComboBoxProperties(Properties).DropDownAutoSize := False;
    TcxLookupComboBoxProperties(Properties).DropDownRows := g_iDropDownRows;
    TcxLookupComboBoxProperties(Properties).ImmediatePost := True; // 即时提交，不用失去焦点
    // 列表模式，  打开提高速度，  但无排序分组等功能
    TcxLookupComboBoxProperties(Properties).GridMode := True;
    TcxLookupComboBoxProperties(Properties).ListOptions.SyncMode := False;
    TcxLookupComboBoxProperties(Properties).IncrementalSearch := True;
    // 关闭自动查询功能，解决中文匹配时的乱码问题  D7不支持UniCode
    // TcxLookupComboBoxProperties(Properties).Grid.DataController.DataModeController.SyncMode := False;
    // TcxLookupComboBoxProperties(Properties).Grid.DataController.DataModeController.GridMode := False;
    // TcxLookupComboBoxProperties(Properties).Revertable := True;鼠标双击选择下一个
    TcxLookupComboBoxProperties(Properties).ValidateOnEnter := True; // 回车提交
    TcxLookupComboBoxProperties(Properties).ClearKey := ShortCut(Ord('D'), [ssCtrl]); // scCtrl;
    if g_bLkRight then
      TcxLookupComboBoxProperties(Properties).PopupAlignment := taRightJustify;
    if FLookupID <> 'td_unitprice' then
    begin
      FOldPopup := TcxLookupComboBoxProperties(Properties).OnPopup;
      FOldCloseup := TcxLookupComboBoxProperties(Properties).OnCloseUp;
      TcxLookupComboBoxProperties(Properties).OnInitPopup := DBComboBoxColumnInitPopup;
      TcxLookupComboBoxProperties(Properties).OnEditValueChanged := DBComboBoxColumnEditValueChange;
      TcxLookupComboBoxProperties(Properties).OnPopup := DBComboBoxColumnPopup;
      TcxLookupComboBoxProperties(Properties).OnCloseUp := DBComboBoxColumnCloseup;
      TcxLookupComboBoxProperties(Properties).OnValidate := DBComboBoxColumnValidate;
      TcxLookupComboBoxProperties(Properties).DataController.OnSortingChanged := SortingChanged;
    end;
    if (FLookupID <> '') and (FLookupID <> 'td_unitprice') and (FLookupID <> 'ts_traderlev') then
    begin
      popMenu := TPopupMenu.Create(AOwner.Owner);
      muAdd := TMenuItem.Create(popMenu);
      muAdd.Caption := '新增 Alt+I(Ins)';
      muAdd.OnClick := pmNewClick;
      popMenu.Items.Add(muAdd);
      muEdit := TMenuItem.Create(popMenu);
      muEdit.Caption := '修改 Alt+E';
      muEdit.OnClick := pmEditClick;
      popMenu.Items.Add(muEdit);
      muFind := TMenuItem.Create(popMenu);
      muFind.Caption := '查找 Alt+F(F5)';
      muFind.OnClick := pmFindClick;
      popMenu.Items.Add(muFind);
      // if (FLookupID <> 'td_product') and (FLookupID <> 'td_depot') then
      begin
        muEmpty := TMenuItem.Create(popMenu);
        muEmpty.Caption := '置空 Ctrl+D';
        muEmpty.OnClick := pmEmptyClick;
        popMenu.Items.Add(muEmpty);
      end;
      with TcxLookupComboBoxProperties(Properties).Buttons.Add do
        Kind := bkEllipsis;
      TcxLookupComboBoxProperties(Properties).OnButtonClick := ButtonClick;
    end;
    Properties.EndUpdate;
  end;
  if (FShowField = '') or (FShowField = edtColumn.DataBinding.FieldName) or
    (TcxGridDBTableView(edtColumn.GridView).GetColumnByFieldName(FShowField) = nil) then
  begin
    OpenData;
  end
  else if not ListDataSet.Active and
    (TcxGridDBTableView(edtColumn.GridView).GetColumnByFieldName(FShowField) <> nil)
  then
  begin
    // 未打开数据字典时，使用查询语句对应的名称字段显示，
    // 节省打开窗体时就打开字典的时间
    edtColumn.OnGetDisplayText := OnDisplayText;
  end;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnCloseup(Sender: TObject);
begin
  // TcxLookupComboBoxProperties(edtComboBox.Properties).Grid.BeginUpdate;
  if Assigned(FOldCloseup) then
    FOldCloseup(Sender);
  try
    ListDataSet.DisableControls;
    // ListDataSet.OnFilterRecord := FOldFilterRecord;
    // ListDataSet.Filter := '';
    ListDataSet.Filtered := False;
  finally
    ListDataSet.EnableControls;
  end;
  FFindMemo.Visible := False;
  FFindMemo.Tag := 0;
  // if FSetValue then
  // TcxLookupComboBoxProperties(edtComboBox.Properties).DataController.Post();
  FSetValue := False;
  // 列表中滚动鼠标滚轮，在lookupcombobox中下拉数据滚动关闭
  TcxLookupComboBoxProperties(edtColumn.Properties).UseMouseWheel := False;
  // TcxLookupComboBoxProperties(edtComboBox.Properties).Grid.EndUpdate;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnEditValueChange(Sender: TObject);
begin
  FSetValue := True;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnFilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
  sPy, sListField, sTmpListField: string;
  sFilter, sOld: widestring;
  iPos: integer;
begin
  if edtComboBox.Properties = nil then
    exit;
  if Assigned(FOldFilterRecord) then
  begin
    FOldFilterRecord(DataSet, Accept);
    if not Accept then
      exit;
  end;
  sFilter := LowerCase(FFindText);
  if (sFilter <> '') and (edtComboBox.Properties.ListColumns.Count > 0) then
  begin
    Accept := False;
    sTmpListField := edtComboBox.Properties.ListFieldNames;
    while Pos(';', sTmpListField) > 0 do
    begin
      iPos := Pos(';', sTmpListField);
      sListField := Copy(sTmpListField, 0, iPos - 1);
      sPy := LowerCase(ChnToPY(DataSet.FieldByName(sListField).AsString));
      sOld := LowerCase(DataSet.FieldByName(sListField).AsString);
      Accept := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
      sTmpListField := Copy(sTmpListField, iPos + 1, Length(sTmpListField) - iPos);
      if Accept then
        exit;
    end;
    if sTmpListField <> '' then
    begin
      sPy := LowerCase(ChnToPY(DataSet.FieldByName(sTmpListField).AsString));
      sOld := LowerCase(DataSet.FieldByName(sTmpListField).AsString);
      Accept := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
    end;
    if Accept then
      exit;
  end
  else
    Accept := True;
  if Assigned(FOldFilterRecord) then
    FOldFilterRecord(DataSet, Accept);
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  sText, sSelText: widestring;
begin
  if (Pos('td_product', FLookupID) > 0) and (GetSysParamValue('productinput') = '1') and
    (edtComboBox.Text = ' ') then
  begin
    muFind.Click;
    exit;
  end;
  if (ssAlt in Shift) and (Key = Ord('F')) then // if (ssCtrl in Shift) then
  begin
    pmFindClick(nil);
  end
  else if (ssAlt in Shift) and (Key = Ord('E')) then
  begin
    pmEditClick(nil);
  end
  else if (ssAlt in Shift) and (Key = Ord('I')) then
  begin
    pmNewClick(nil);
  end
  else
  begin
    case Key of
      VK_UP:
        ;
      VK_DOWN:
        ;
      VK_F2:
        begin
          ListDataSet.OnFilterRecord := DBComboBoxColumnFilterRecord;
          ListDataSet.Filtered := True;
        end;
      VK_RETURN:
        begin
          if GetSysParamValue('productinput') = '1' then
          begin
            ListDataSet.DisableControls;
            FOldFilter := ListDataSet.Filter;
            FOldFiltered := ListDataSet.Filtered;
            ListDataSet.OnFilterRecord := DBComboBoxColumnFilterRecord;
            ListDataSet.Filtered := True;
            ListDataSet.EnableControls;
          end
          else if (ListDataSet.Eof and not FindPy(FFindText)) or (Trim(edtComboBox.Text) = '') then
          begin
            ListDataSet.FindKey([edtField.Value]);
            edtColumn.DataBinding.DataController.DataSource.DataSet.Cancel;
            // TWinControl(Sender).Perform(WM_KEYDOWN, VK_ESCAPE, 0);
            // if edtField<>nil then
            // SetFieldValNoChg(edtField.DataSet, edtField.FieldName, edtField.OldValue);
            // edtComboBox.Properties.Changed;
          end
          else
          begin
            ListDataSet.OnFilterRecord := FOldFilterRecord;
            // if edtField <> nil then
            // edtField.Value := ListDataSet.FieldByName(edtComboBox.Properties.KeyFieldNames).Value;
            // TWinControl(Sender).Perform(WM_KEYDOWN, VK_TAB, 0);
          end;
        end;
      VK_INSERT:
        pmNewClick(nil);
      VK_F5:
        begin
          ListDataSet.DisableControls;
          FOldFilter := ListDataSet.Filter;
          FOldFiltered := ListDataSet.Filtered;
          // if not Assigned(FOldFilterRecord) then
          // FOldFilterRecord := ListDataSet.OnFilterRecord;
          // ListDataSet.Filter := '';
          // ListDataSet.Filtered := False;
          ListDataSet.OnFilterRecord := DBComboBoxColumnFilterRecord;
          ListDataSet.Filtered := True;
          ListDataSet.EnableControls;
          if (ListDataSet.RecordCount = 1) and (GetSysParamValue('productinput') = '1') then
          begin
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_RETURN, 0);
          end
        end;
    else
      begin
        sText := edtComboBox.Text;
        sSelText := Copy(sText, edtComboBox.SelStart + 1, edtComboBox.SelLength); // 处理中文
        // if (Length(sText) > 1) and not (sText[Length(sText)] < #160) then //判断最后一个字是否中文
        // begin
        // if sText[Length(sText)] = sText[Length(sText) - 1] then //判断最后两个字中文是否相同，是的话去掉一个
        // begin
        // sText := Copy(sText, 1, Length(sText) - 1);
        // if edtComboBox.Text <> sText + sSelText then
        // begin
        // edtComboBox.Text := sText + sSelText;
        // edtComboBox.SelStart := Length(sText);
        // edtComboBox.SelLength := Length(sSelText);
        // end;
        // end;
        // end;
        // //sSelText := edtComBoBox.SelText;            AnsiReplaceStr    StringReplace(sText, sSelText, '', [])
        FFindHint := QuotedStr(Trim(AnsiReplaceStr(sText, sSelText, ''))) + #13#10 + 'F5过滤';
        FFindText := Trim(AnsiReplaceStr(sText, sSelText, ''));
        ShowFindHint;
        if ((Pos('td_product', FLookupID) > 0) and (GetSysParamValue('productinput') = '2')) or
          ((Pos('tfd_subject', FLookupID) > 0) and (GetSysParamValue('subjectinput') = '1')) or
          ((Pos('td_trader', FLookupID) > 0) and (GetSysParamValue('traderinput') = '1')) then
        begin
          ListDataSet.DisableControls;
          FOldFilter := ListDataSet.Filter;
          FOldFiltered := ListDataSet.Filtered;
          // ListDataSet.Filtered := False;
          ListDataSet.OnFilterRecord := DBComboBoxColumnFilterRecord;
          ListDataSet.Filtered := True;
          ListDataSet.EnableControls;
        end
        else
        begin
          // FFindHint := QuotedStr(Trim(StringReplace(edtComBoBox.Text, edtComboBox.SelText, '', []))) + #13#10 + 'F5过滤';
          // FFindText := LowerCase(StringReplace(edtComBoBox.Text, edtComboBox.SelText, '', []));

          if Trim(edtComboBox.Text) = '' then
          begin
            ListDataSet.OnFilterRecord := FOldFilterRecord;
            if FFilter = '' then
              ListDataSet.Filter := FOldFilter
            else
              ListDataSet.Filter := FFilter;
            if ListDataSet.Filtered <> FOldFiltered then
              ListDataSet.Filtered := FOldFiltered;
            // if FixDropFilter then
            // ListDataSet.Filtered := True;
            exit;
          end;
          if (FLookupID = 'td_product') and ((GetSysParamValue('productinput') = '2') or
            (GetSysParamValue('productinput') = '1')) then
            exit;
          ListDataSet.DisableControls;
          // ListDataSet.Filter := '';
          // ListDataSet.Filtered := False;
          ListDataSet.First;
          while not ListDataSet.Eof do
          begin
            if FindPy(FFindText) then
              break;
            ListDataSet.Next;
          end;
          ShowFindHint;
          // 下拉列表获取光标
          ListDataSet.EnableControls;
          if ListDataSet.RecNo = 1 then
          begin
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
          end
          else if ListDataSet.Eof then
          begin
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
          end
          else
          begin
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_UP, 0);
            SendMessage(edtComboBox.Properties.Grid.Handle, WM_KEYDOWN, VK_DOWN, 0);
          end;
        end;
      end;
    end;
  end;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnPopup(Sender: TObject);
var
  P: TPoint;
  pForm: TPoint;
begin
  TcxLookupComboBoxProperties(edtComboBox.Properties).DropDownHeight := 150;
  if g_iLkHeight > 0 then
    TcxLookupComboBoxProperties(edtComboBox.Properties).DropDownHeight := g_iLkHeight;
  if g_iLkWidth > 0 then
    TcxLookupComboBoxProperties(edtComboBox.Properties).DropDownWidth := g_iLkWidth;

  if Assigned(FOldPopup) then
    FOldPopup(Sender);
  if FFilter <> ListDataSet.Filter then
    ListDataSet.Filter := FFilter;
  if ListDataSet.Filter <> '' then
    ListDataSet.Filtered := True;

  if (Trim(ListDataSet.KeyFields) <> '') and (edtField <> nil) and not edtField.IsNull then
  begin
    ListDataSet.FindKey([edtField.Value]);
  end;
  FFindMemo.Tag := 1;
  if (g_iLKTop > 0) and (FParentForm <> nil) then
  begin
    P.X := 0;
    P.Y := 0;
    pForm.X := 0;
    pForm.Y := 0;
    Windows.ClientToScreen(edtComboBox.Handle, P);
    Windows.ClientToScreen(FParentForm.Height, pForm);
    edtComboBox.Properties.Grid.Parent.Top := P.Y + pForm.Y + g_iLKTop;
  end;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnInitPopup(Sender: TObject);
begin
  if ListDataSource.DataSet = nil then
    ListDataSource.DataSet := ListDataSet;
  OpenData;
  ListDataSet.OnFilterRecord := FOldFilterRecord;
  if FFilter <> ListDataSet.Filter then
    ListDataSet.Filter := FFilter;
  if ListDataSet.Filter <> '' then
    ListDataSet.Filtered := True;
  if edtComboBox = nil then
  begin
    edtComboBox := TcxLookupComboBox(Sender);
    edtComboBox.OnDblClick := DBComboBoxColumnDblClick;
    if (FParentForm = nil) and (edtComboBox.Parent <> nil) then
      FFindMemo.Parent := edtComboBox.Parent;
  end;
  edtComboBox.OnKeyUp := DBComboBoxColumnKeyUp;
  TcxLookupComboBoxProperties(edtColumn.Properties).UseMouseWheel := True;
  // edtComboBox.Properties.OnChange := DBComboBoxColumnChange;
end;

destructor TcxDBColumnOnChange.Destroy;
begin
  if (FListField <> nil) and (FListField.Count > 0) then
  begin
    FListField.Clear;
    FreeAndNil(FListField);
  end;
  FreeAndNil(muAdd);
  FreeAndNil(muEdit);
  FreeAndNil(muFind);
  FreeAndNil(muEmpty);
  FreeAndNil(popMenu);
  inherited;
end;

function TcxDBColumnOnChange.FindPy(const sSubStr: widestring): Boolean;
var
  sPy, sListField, sTmpListField: string;
  sFilter, sOld: widestring;
  iPos: integer;
begin
  sFilter := LowerCase(sSubStr);
  if (sFilter <> '') and (FListField.Count > 0) then
  begin
    if (Pos('tfd_subject', FLookupID) > 0) and (IsDigit(sFilter)) then
    begin
      Result := Copy(ListDataSet.FieldByName('subcode').AsString, 1, Length(sFilter)) = sFilter;
      exit;
    end;
    Result := False;
    sTmpListField := Trim(TcxLookupComboBoxProperties(edtColumn.Properties).ListFieldNames);
    while (Pos(';', sTmpListField) > 0) or (sTmpListField <> '') do
    begin
      iPos := Pos(';', sTmpListField);
      if iPos > 0 then
        sListField := Copy(sTmpListField, 0, iPos - 1)
      else
      begin
        sListField := sTmpListField;
        sTmpListField := '';
      end;
      sPy := LowerCase(ChnToPY(ListDataSet.FieldByName(sListField).AsString));
      sOld := LowerCase(ListDataSet.FieldByName(sListField).AsString);
      Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
      sTmpListField := Copy(sTmpListField, iPos + 1, Length(sTmpListField) - iPos);
      if Result then
        exit;
    end;
    if sTmpListField <> '' then
    begin
      sPy := LowerCase(ChnToPY(ListDataSet.FieldByName(sTmpListField).AsString));
      sOld := LowerCase(ListDataSet.FieldByName(sTmpListField).AsString);
      Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
    end;
    // for iPos := 0 to FListField.Count - 1 do
    // begin
    // sPy := LowerCase(ChnToPY(ListDataSet.FieldByName(FListField.Strings[iPos]).AsString));
    // sOld := LowerCase(ListDataSet.FieldByName(FListField.Strings[iPos]).AsString);
    // Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
    // end;
    if Result then
      exit;
  end
  else
    Result := True;
end;

function TcxDBColumnOnChange.GetDataSet: TDataSet;
begin
  if FDataSet = nil then
  begin
    if edtColumn.DataBinding.DataController.DataSource.DataSet <> nil then
      FDataSet := edtColumn.DataBinding.DataController.DataSource.DataSet;
  end;
  Result := FDataSet;
end;

function TcxDBColumnOnChange.GetedtField: TField;
begin
  FedtField := nil;
  if DataSet.Active and (DataSet.FindField(edtColumn.DataBinding.FieldName) <> nil) then
    FedtField := DataSet.FieldByName(edtColumn.DataBinding.FieldName);
  Result := FedtField;
end;

procedure TcxDBColumnOnChange.pmEditClick(Sender: TObject);
begin
  PopLookup(et_Modify);
end;

procedure TcxDBColumnOnChange.pmEmptyClick(Sender: TObject);
begin
  if not(DataSet.State in [dsEdit, dsInsert]) then
    DataSet.Edit;
  edtField.Clear;
end;

procedure TcxDBColumnOnChange.pmFindClick(Sender: TObject);
begin
  PopLookup(et_Browser);
end;

procedure TcxDBColumnOnChange.pmNewClick(Sender: TObject);
begin
  PopLookup(et_New);
end;

procedure TcxDBColumnOnChange.PopLookup(EditType: TEditType);
var
  oForm: TfrmBaseChildcxGridBtnQueryForm;
  dForm: TfrmBaseDataForm;
  tForm: TFrmBaseTreeForm;
  // AQuery: TUniQuery;
  sForm, sFormCaption, sID: string;
  DllFun: TDLLFunction;
  iForm, iProductID: integer;
begin
  // if GetADOQuery(AQuery, 'select * from ts_DynamicEditFormMst where et_Table_name=' + QuotedStr(FLookupID)) > 0 then
  // begin
  // sForm := AQuery.FieldByName('et_editformid').AsString;
  // sFormCaption := AQuery.FieldByName('et_formcaption').AsString;
  // end
  // else
  sFormCaption := '';
  if (LowerCase(FLookupID) = 'td_trader') and (FFilter <> '') then
  begin
    if Pos('client', FFilter) > 0 then
      FLookupID := 'td_traderclient'
    else if Pos('provider', FFilter) > 0 then
      FLookupID := 'td_traderprovider';
  end;
  DllFun := MainDM.GetModuleItemForEdit(FLookupID, sFormCaption);
  if DllFun <> nil then
  begin
    if sFormCaption = '' then
    begin
      sForm := IntToStr(DllFun.ModuleFuncID);
      sFormCaption := DllFun.ModuleFuncName;
    end;
    if not((FLookupID = 'td_product') and (EditType = et_Browser)) then // 单独处理货品查找
      MainDM.GetUserInfo.ActiveDllFunction := DllFun;
    case EditType of
      et_New:
        begin
          if not DllFun.DefaultRights.bAdd then
          begin
            ShowWarningBox('您没有新增' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
      et_Modify:
        begin
          if not DllFun.DefaultRights.bEdit then
          begin
            ShowWarningBox('您没有修改' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
      et_Browser:
        begin
          if not DllFun.DefaultRights.bBrowse then
          begin
            ShowWarningBox('您没有浏览' + sFormCaption + '资料的权限，请联系系统管理员。');
            exit;
          end;
        end;
    end;
  end;
  if (FLookupID = 'td_post') or (FLookupID = 'td_money') or (FLookupID = 'td_filiale') or
    (FLookupID = 'td_workshop') or
    (FLookupID = 'td_workproc') or (FLookupID = 'td_workline') or (FLookupID = 'td_emptype') or
    (FLookupID = 'td_bank')
    or (FLookupID = 'td_servicetype') or (FLookupID = 'td_repairtype') or (FLookupID = 'td_machine') or
    (FLookupID = 'td_commistype') or (FLookupID = 'td_comexpenditure') or (FLookupID = 'td_oa_flow') or
    (FLookupID = 'td_oa_type') or (FLookupID = 'td_oa_items') or (FLookupID = 'td_oa_template') or
    (FLookupID = 'tfd_vouword') or (FLookupID = 'tfd_vousummarytype') then
  begin
    if DllFun = nil then
      exit;
    // if (DataSet.State in [dsEdit, dsInsert]) then
    // DataSet.Post;
    try
      oForm := TfrmBaseChildcxGridBtnQueryForm.Create(nil, sFormCaption, True);
      if CreatesDynamicEditForm(sForm, sFormCaption, DllFun, oForm) then
      begin
        case EditType of
          et_New:
            begin
              oForm.LocateRecord(TMP_ID);
              oForm.btnNew.OnClick(nil);
            end;
          et_Modify:
            begin
              if edtField.IsNull then
                oForm.LocateRecord(-1)
              else
              begin
                oForm.LocateRecord(edtField.AsInteger);
                oForm.btnEdit.Click;
              end;
            end;
          et_Browser:
            begin
              oForm.LocateRecord(-1);
            end;
        end;
        if oForm.ShowModal = mrOk then
        begin
          RefreshData(False);
          SetDropDownFilter(FFilter);
          if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([oForm.GetKeyValue]) then
          begin
            if not(DataSet.State in [dsEdit, dsInsert]) then
              DataSet.Edit;
            edtField.Value := oForm.GetKeyValue;
          end;
          ListDataSet.Filtered := False;
        end;
        oForm := nil; // ShowModal = mrOk 会触发FormDestroy
        // else if oForm <> nil then
        // FreeAndNil(oForm);
      end;
    finally
      // AQuery.Close;
      // FreeAndNil(AQuery);
      FParentForm.BringToFront;
    end;
  end
  else if (FLookupID = 'td_product') or (FLookupID = 'td_trader') or (FLookupID = 'td_traderthird') or
    (FLookupID = 'td_traderclient') or (FLookupID = 'td_traderprovider') or
    (FLookupID = 'td_empmaster') or
    (FLookupID = 'td_depot') or (FLookupID = 'td_balance') or (FLookupID = 'td_account') or
    (FLookupID = 'td_depotkind')
    or (FLookupID = 'td_depottype') or (FLookupID = 'tfd_subject') or
    (FLookupID = 'tfd_subject_all') or
    (FLookupID = 'td_productbomver') or (FLookupID = 'td_productbomvernotrader') or
    (FLookupID = 'td_productbomformula')
  then
  begin
    dForm := nil;
    if (FLookupID = 'td_product') and (EditType = et_Browser) then // 单独处理货品查找
    begin
      // GlobalCallProc('bill', FLookupID + 'dtl', IntToStr(Integer(FParentForm)))
      if (FParentForm <> nil) and (FParentForm.ClassName <> '') then
      begin
        if Pos('Bill', FParentForm.ClassName) > 0 then
        begin
          GlobalCallProc('bill', 'td_productdtl', IntToStr(integer(FParentForm)));
        end
        else
        begin
          ChoiceProductQuery(FParentForm, edtField);
        end;
      end;
    end
    else
    begin
      sID := '-1'; // 浏览
      if EditType = et_New then
        sID := IntToStr(TMP_ID)
      else if EditType = et_Modify then
      begin
        if edtField.IsNull then
          sID := IntToStr(-1)
        else
          sID := edtField.AsString;
      end;
      if Pos('tfd_', FLookupID) > 0 then
        iForm := StrToInt(VarToStr(GetGlobalCallValue('finance', FLookupID, sID)))
      else if (FLookupID = 'td_productbomver') or (FLookupID = 'td_productbomvernotrader') then
      begin
        iProductID := -1;
        if (edtField = nil) or edtField.IsNull then
          sID := IntToStr(TMP_ID)
        else
        begin
          sID := edtField.AsString;
          if edtField.DataSet.FindField('productid') <> nil then
            iProductID := GetFieldVal(edtField.DataSet, 'productid', -1);
        end;
        iForm := StrToInt(VarToStr(GetGlobalCallValue('manufacture', FLookupID,
          VarArrayOf([iProductID, StrToIntDef(sID, -1)]))));
      end
      else if FLookupID = 'td_productbomformula' then
        iForm := StrToInt(VarToStr(GetGlobalCallValue('manufacture', FLookupID, sID)))
      else
        iForm := StrToInt(VarToStr(GetGlobalCallValue('basedata', FLookupID, sID)));
      dForm := TfrmBaseDataForm(iForm);
    end;
    if (dForm = nil) then
      exit;
    if dForm.FormStyle <> fsNormal then
      dForm.FormStyle := fsNormal;
    if (dForm.ShowModal = mrOk) then
    begin
      RefreshData(False);
      SetDropDownFilter(FFilter);
      if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([dForm.GetKeyValue]) then
      begin
        if not(DataSet.State in [dsEdit, dsInsert]) then
          DataSet.Edit;
        edtField.Value := dForm.GetKeyValue;
      end;
      ListDataSet.Filtered := False;
      dForm := nil; // ShowModal = mrOk 会触发FormDestroy
    end
    else if dForm <> nil then
      FreeAndNil(dForm)
  end
  else if (FLookupID = 'td_dept') or (FLookupID = 'td_expend') or (FLookupID = 'td_area') or
    (FLookupID = 'td_producttype') or (FLookupID = 'td_tradertype') or
    (FLookupID = 'td_productbompart') then
  begin
    sID := '-1'; // 浏览
    if EditType = et_New then
      sID := IntToStr(TMP_ID)
    else if EditType = et_Modify then
    begin
      if edtField.IsNull then
        sID := IntToStr(-1)
      else
        sID := edtField.AsString;
    end;
    iForm := StrToInt(VarToStr(GetGlobalCallValue('basedata', FLookupID, sID)));
    tForm := TFrmBaseTreeForm(iForm);
    if tForm = nil then
      exit;
    tForm.Selected := True;
    if (tForm.ShowModal = mrOk) then
    begin
      RefreshData(False);
      SetDropDownFilter(FFilter);
      if (Trim(ListDataSet.KeyFields) <> '') and ListDataSet.FindKey([tForm.GetKeyValue]) then
      begin
        if not(DataSet.State in [dsEdit, dsInsert]) then
          DataSet.Edit;
        edtField.Value := tForm.GetKeyValue;
      end;
      ListDataSet.Filtered := False;
      tForm := nil; // ShowModal = mrOk 会触发FormDestroy
    end
    else if tForm <> nil then
      FreeAndNil(tForm); // 执行此代码会报错 Abstract Error.
  end;
end;

procedure TcxDBColumnOnChange.RefreshData(RefreshLookupData: Boolean);
begin
  if RefreshLookupData then
  begin
    ListLookup.Close;
    ListLookup.Open;
  end;
  ListDataSet.Close;
  ListDataSet.Open;
end;

procedure TcxDBColumnOnChange.SetDropDownFilter(const Value: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND});
begin
  FFilter := Value;
  try
    if (FFilter <> '') and (FFilter <> ListDataSet.Filter) then
    begin
      ListDataSet.Filtered := False;
      ListDataSet.Filter := FFilter;
    end;
    if not FixDropFilter then // 不是固定过滤条件时，在弹出下拉列表时做过滤处理
      exit;
    ListDataSet.DisableControls;
    if not ListDataSet.Filtered then
    begin
      try
        ListDataSet.Filtered := True;
      except
        on E: Exception do
        begin
          ShowErrorBox('设置下拉过滤错误，错误信息：' + E.Message);
        end;
      end;
    end;
  finally
    ListDataSet.EnableControls;
  end;
end;

procedure TcxDBLookupOnKeyUp.RefreshData(RefreshLookupData: Boolean = True);
begin
  // if RefreshLookupData then
  // begin
  // ListLookup.Close;
  // ListLookup.Open;
  // end;
  ListDataSet.Close;
  ListDataSet.Open;
end;

procedure TcxDBLookupOnKeyUp.SetDropDownFilter(const Value: {$IF CompilerVersion>23.0} String
{$ELSE} widestring{$IFEND});
begin
  if (ListDataSet = nil) then
    exit;
  FFilter := Value;
  if FFixDropFilter then
    try
      ListDataSet.DisableControls;
      if (FFilter <> '') and (FFilter <> ListDataSet.Filter) then
      begin
        ListDataSet.Filtered := False;
        ListDataSet.Filter := FFilter;
      end;
      if not ListDataSet.Filtered then
      begin
        try
          ListDataSet.Filtered := True;
        except
          on E: Exception do
          begin
            ShowErrorBox('设置下拉过滤错误，错误信息：' + E.Message);
          end;
        end;
      end;
    finally
      ListDataSet.EnableControls;
    end;
  edtComboBox.Properties.DataChanged;
end;

procedure TcxDBColumnOnChange.ShowFindHint;
var
  pMemo, pCombox: TPoint;
begin
  if (FFindMemo.Parent <> nil) and (FFindMemo.Parent is tForm) and (edtComboBox.Parent <> nil) then
  begin
    pCombox.X := 0;
    pCombox.Y := 0;

    pCombox := edtComboBox.ClientToScreen(pCombox);
    pMemo := FParentForm.ScreenToClient(pCombox);
    FFindMemo.Left := pMemo.X;
    FFindMemo.Top := pMemo.Y - 30;
  end
  else
  begin
    FFindMemo.Left := edtComboBox.Left;
    FFindMemo.Top := edtComboBox.Top - 30;
  end;
  FFindMemo.Text := FFindHint;
  FFindMemo.Visible := (FFindHint <> '') and edtComboBox.Focused and (FFindMemo.Tag = 1);
  if FFindHint = '' then
  begin
    ListDataSet.OnFilterRecord := FOldFilterRecord;
    // ListDataSet.Filter := FOldFilter;
    // ListDataSet.Filtered := FOldFiltered;
  end;
  // FFindMemo.BringToFront;
end;

procedure TcxDBColumnOnChange.OnDisplayText(Sender: TcxCustomGridTableItem;
  ARecord: TcxCustomGridRecord;
  var AText: string);
begin
  if (Sender <> nil) and (ARecord <> nil) and (edtColumn <> nil) and
    (TcxGridDBTableView(edtColumn.GridView).GetColumnByFieldName(FShowField) <> nil) then
    AText := Trim(VarAsType(ARecord.displaytexts[TcxGridDBTableView(edtColumn.GridView)
      .GetColumnByFieldName(FShowField)
      .Index], varString));
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnValidate(Sender: TObject; var DisPlayValue: Variant;
  var ErrorText: TCaption; var Error: Boolean);
begin
  ListDataSet.OnFilterRecord := FOldFilterRecord; // 增加此代码会提高速度
end;

procedure TcxDBColumnOnChange.SortingChanged(Sender: TObject);
var
  sField, sIndex: string;
begin
  sField := '';
  sIndex := '';
  if edtComboBox.Properties.DataController.GetSortingItemCount > 0 then
  begin
    sField := edtComboBox.Properties.DataController.GetItemFieldName
      (edtComboBox.Properties.DataController.GetSortingItemIndex(0));
    if ListDataSet.IndexFieldNames = sField then
      sIndex := sField + ' desc'
    else
      sIndex := sField;
  end;
  ListDataSet.IndexFieldNames := sIndex;
end;

procedure TcxDBColumnOnChange.DBComboBoxColumnDblClick(Sender: TObject);
begin
  pmFindClick(nil);
end;

procedure TcxDBColumnOnChange.DblClick;
begin
  pmFindClick(nil);
end;

procedure TcxDBColumnOnChange.OpenData;
begin
  if not ListDataSet.Active then
  begin
    if (LowerCase(FLookupID) = 'td_product') and (GetSysParamValue('bomsecret') = '1') and
      (not MainDM.GetModuleRight(1, 7005, rtExec)) then // 配方保密
    begin
      ListDataSet.SQL.Text :=
        'select code,case when kind<3 then code else name end as name,spec,* from td_product order by code';
    end;
    ListDataSet.Open;
  end;
end;

{ TcxDBColumnComBoxOnChange }

constructor TcxDBColumnComBoxOnChange.Create(ParentForm: TfrmBaseForm; AOwner: TComponent;
  LookupID, LookupParam, TableName: string);
var
  oLookupDataSet: TDataSet;
  sFieldName, sLookupID, sAddField, sKeyField, Param: string;
begin
  FLookupID := LowerCase(LookupID);
  FParentForm := ParentForm;
  DropDownList := TStringList.Create;
  edtColumn := TcxGridDBBandedColumn(AOwner);
  sFieldName := edtColumn.DataBinding.FieldName;
  sLookupID := LowerCase(LookupID);

  if (Pos('ts_kind', sLookupID) > 0) and (sLookupID <> 'ts_kind') then
  begin
    Param := Copy(sLookupID, 8, Length(sLookupID) - 7);
    sLookupID := 'ts_kind';
  end;
  oLookupDataSet := GetLookupTable(sLookupID, '');
  sAddField := PLookupTableInfo(oLookupDataSet.Tag)^.ShowField;
  sKeyField := PLookupTableInfo(oLookupDataSet.Tag)^.KeyField;
  with edtColumn do
  begin
    PropertiesClass := TcxComboBoxProperties;
    TcxComboBoxProperties(Properties).IncrementalSearch := False;
    TcxComboBoxProperties(Properties).ImmediatePost := True;
    TcxComboBoxProperties(Properties).OnChange := DBComboBoxColumnChange;
    TcxComboBoxProperties(Properties).DropDownSizeable := True;
    TcxComboBoxProperties(Properties).DropDownRows := g_iDropDownRows;
  end;
  if (sLookupID = 'ts_custom') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'tablename=' + QuotedStr(TableName) + ' and fieldname=' +
      QuotedStr(sFieldName);
    oLookupDataSet.Filtered := True;
  end
  else if (sLookupID = 'ts_kind') then
  begin
    oLookupDataSet.Filtered := False;
    oLookupDataSet.Filter := 'kind=' + Param;
    oLookupDataSet.Filtered := True;
    if oLookupDataSet.FieldByName('iscustom').AsBoolean then
    begin
      TcxComboBoxProperties(edtColumn.Properties).DropDownListStyle := lsEditList;
    end
    else
      TcxComboBoxProperties(edtColumn.Properties).DropDownListStyle := lsFixedList;
  end;

  oLookupDataSet.First;
  while not oLookupDataSet.Eof do
  begin
    TcxComboBoxProperties(edtColumn.Properties)
      .Items.AddObject(oLookupDataSet.FieldByName(sAddField).AsString,
      TObject(oLookupDataSet.FieldByName(sKeyField).AsString));
    DropDownList.AddObject(oLookupDataSet.FieldByName(sAddField).AsString,
      TObject(oLookupDataSet.FieldByName(sKeyField).AsString));
    oLookupDataSet.Next;
  end;
  oLookupDataSet.Filtered := False;
  oLookupDataSet.Filter := '';
  oLookupDataSet.First;
end;

procedure TcxDBColumnComBoxOnChange.DBComboBoxColumnChange(Sender: TObject);
var
  i, iFindNo: integer;
begin
  if (FLookupID = 'td_product') and (GetSysParamValue('productinput') = '2') then
    exit;

  if edtComboBox = nil then
    edtComboBox := TcxComboBox(Sender);

  // if edtComboBox.Properties.Items.Count = 0 then
  // Exit;
  edtComboBox.Properties.Items.Clear;

  if Trim(edtComboBox.Text) = '' then
  begin
    for i := 0 to DropDownList.Count - 1 do
      edtComboBox.Properties.Items.AddObject(DropDownList.Strings[i], DropDownList.Objects[i]);
    edtComboBox.Properties.ValidateOnEnter := False;
    // edtComboBox.Properties.ImmediatePost := False; //即时提交，不用失去焦点
    exit;
  end;

  iFindNo := -1;
  for i := 0 to DropDownList.Count - 1 do
  begin
    if FindPy(edtComboBox.Text, DropDownList.Strings[i]) or
      (Pos(edtComboBox.Text, DropDownList.Strings[i]) > 0) then
    begin
      edtComboBox.Properties.Items.AddObject(DropDownList.Strings[i], DropDownList.Objects[i]);
      iFindNo := i;
    end;
  end;
  // edtComboBox.ItemIndex := iFindNo;
  edtComboBox.Properties.ValidateOnEnter := iFindNo > -1;
  // edtComboBox.Properties.ImmediatePost := iFindNo > -1;

  // for i := 0 to edtComboBox.Properties.Items.Count - 1 do
  // begin
  // if FindPy(edtComboBox.Text, edtComboBox.Properties.Items.Strings[i]) then
  // begin
  // iFindNo := i;
  // Break;
  // end;
  // end;
  // edtComboBox.ItemIndex := iFindNo;
  // edtComboBox.Properties.ValidateOnEnter := iFindNo > 0;
  // edtComboBox.Properties.ImmediatePost := iFindNo > 0;
end;

destructor TcxDBColumnComBoxOnChange.Destroy;
begin
  DropDownList.Clear;
  FreeAndNil(DropDownList);
  inherited;
end;

function FindPy(const sSubStr, sListStr: widestring): Boolean;
var
  sPy: string;
  sFilter, sOld: widestring;
begin
  sFilter := LowerCase(sSubStr);
  if (sFilter <> '') then
  begin
    sPy := LowerCase(ChnToPY(sListStr));
    sOld := LowerCase(sListStr);
    Result := (AnsiPos(sFilter, sPy) > 0) or (AnsiPos(sFilter, sOld) > 0);
  end
  else
    Result := True;
end;

function TcxDBColumnComBoxOnChange.GetedtField: TField;
begin
  if FedtField = nil then
  begin
    if edtColumn.DataBinding.DataController.DataSource.DataSet.Active and
      (edtColumn.DataBinding.DataController.DataSource.DataSet.FindField
      (edtColumn.DataBinding.FieldName) <> nil) then
      FedtField := edtColumn.DataBinding.DataController.DataSource.DataSet.FieldByName
        (edtColumn.DataBinding.FieldName);
  end;
  Result := FedtField;
end;

procedure TcxDBLookupOnKeyUp.ShowFindHint;
var
  pMemo, pCombox: TPoint;
begin
  if (FFindMemo.Parent <> nil) and (FFindMemo.Parent is tForm) then
  begin
    pCombox.X := 0;
    pCombox.Y := 0;
    pCombox := edtComboBox.ClientToScreen(pCombox);
    pMemo := ParentForm.ScreenToClient(pCombox);
    FFindMemo.Left := pMemo.X;
    FFindMemo.Top := pMemo.Y - 30;
  end
  else
  begin
    FFindMemo.Left := edtComboBox.Left;
    FFindMemo.Top := edtComboBox.Top - 30;
  end;
  FFindMemo.Text := FFindHint;
  FFindMemo.Visible := (FFindHint <> '') and edtComboBox.Focused and (FFindMemo.Tag = 1);
  if FFindHint = '' then
  begin
    ListDataSet.OnFilterRecord := FOldFilterRecord;
    // ListDataSet.Filter := FOldFilter;
    // ListDataSet.Filtered := FOldFiltered;
  end;
  FFindMemo.BringToFront;
end;

procedure TcxDBLookupOnKeyUp.ShowNameClick(Sender: TObject);
begin
  if FShowName <> nil then
    FShowName.Visible := False;
  if (edtComboBox <> nil) and edtComboBox.CanFocus then
    edtComboBox.SetFocus;
end;

end.
